diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 177165ad..8e132f55 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,10 +12,10 @@ on: jobs: build: - runs-on: macos-12 + runs-on: macos-13 env: - XCODE: /Applications/Xcode_13.4.1.app - XCODE_SDK: iphoneos15.5 + XCODE: /Applications/Xcode_14.3.1.app + XCODE_SDK: iphoneos16.4 steps: - uses: actions/checkout@v3 - name: Select Xcode Version diff --git a/.jazzy.yaml b/.jazzy.yaml index 5c2a0d0a..856f1ea2 100644 --- a/.jazzy.yaml +++ b/.jazzy.yaml @@ -8,8 +8,8 @@ theme: apple min_acl: public sdk: iphoneos module: Sora -module_version: 2023.1.0 -swift_version: 5.8 +module_version: 2023.2.0 +swift_version: 5.8.1 xcodebuild_arguments: - -parallelizeTargets - -sdk diff --git a/.swift-version b/.swift-version index 7acd1cb0..3659ea2f 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -5.5.1 +5.8 diff --git a/CHANGES.md b/CHANGES.md index b0a7567b..c4bc5257 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,7 +11,27 @@ ## develop -## 2023.1.0 +## 2023.2.0 + +- [UPDATE] システム条件を変更する + - macOS 13.4.1 以降 + - WebRTC SFU Sora 2023.2.0 以降 + - Xcode 14.3.1 + - Swift 5.8.1 + - CocoaPods 1.12.1 以降 + - @miosakuma +- [UPDATE] WebRTC 115.5790.7.0 に上げる + - @szktty @miosakuma +- [ADD] 転送フィルター機能を追加する + - `Configuration` に `forwardingFilter` を追加する + - @szktty +- [ADD] 映像コーデックパラメーターの設定を追加する + - `Configuration` に `videoVp9Params`, `videoAv1Params`, `videoH264Params` を追加する + - @miosakuma +- [ADD] サイマルキャストを VP9 / AV1 に対応する + - @szktty + +## 2023.2.0 - [UPDATE] WebRTC 112.5615.1.0 に上げる - @miosakuma diff --git a/Gemfile b/Gemfile index eafffe8f..744ec8ca 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,4 @@ # frozen_string_literal: true source "https://rubygems.org" -gem 'cocoapods' , '1.12.0' +gem 'cocoapods' , '1.12.1' diff --git a/Package.swift b/Package.swift index 1fce2b17..ef945ea9 100644 --- a/Package.swift +++ b/Package.swift @@ -3,7 +3,7 @@ import Foundation import PackageDescription -let file = "WebRTC-112.5615.1.0/WebRTC.xcframework.zip" +let file = "WebRTC-115.5790.7.0/WebRTC.xcframework.zip" let package = Package( name: "Sora", @@ -16,7 +16,7 @@ let package = Package( .binaryTarget( name: "WebRTC", url: "https://github.com/shiguredo/sora-ios-sdk-specs/releases/download/\(file)", - checksum: "d4cd24535d4a4122cd8734284f398edeee13a9cff785f8c9522bee3689bbaedf" + checksum: "1a852818dc15280f3ded5d16cc52fb30c91515c9f08b26d2dcaea68a7de414fc" ), .target( name: "Sora", diff --git a/Podfile b/Podfile index e6147d4a..4af7346e 100644 --- a/Podfile +++ b/Podfile @@ -5,5 +5,5 @@ platform :ios, '13.0' target 'Sora' do use_frameworks! - pod 'WebRTC', '112.5615.1.0' + pod 'WebRTC', '115.5790.7.0' end diff --git a/Podfile.dev b/Podfile.dev index cbf8a451..7f9da9ac 100644 --- a/Podfile.dev +++ b/Podfile.dev @@ -5,7 +5,7 @@ platform :ios, '13.0' target 'Sora' do use_frameworks! - pod 'WebRTC', '112.5615.1.0' - pod 'SwiftLint', '0.45.1' - pod 'SwiftFormat/CLI', '0.49.0' + pod 'WebRTC', '115.5790.7.0' + pod 'SwiftLint', '0.51.0' + pod 'SwiftFormat/CLI', '0.51.6' end diff --git a/README.md b/README.md index 3146aa37..705a313a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Sora iOS SDK -[![libwebrtc](https://img.shields.io/badge/libwebrtc-112.5615-blue.svg)](https://chromium.googlesource.com/external/webrtc/+/branch-heads/5615) +[![libwebrtc](https://img.shields.io/badge/libwebrtc-115.5790-blue.svg)](https://chromium.googlesource.com/external/webrtc/+/branch-heads/5790) [![GitHub tag](https://img.shields.io/github/tag/shiguredo/sora-ios-sdk.svg)](https://github.com/shiguredo/sora-ios-sdk) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) @@ -20,11 +20,11 @@ Please read https://github.com/shiguredo/oss before use. - iOS 13 以降 - アーキテクチャ arm64 (シミュレーターの動作は未保証) -- macOS 13.3 以降 -- Xcode 14.3 -- Swift 5.8 -- CocoaPods 1.12.0 以降 -- WebRTC SFU Sora 2022.2.0 以降 +- macOS 13.4.1 以降 +- Xcode 14.3.1 +- Swift 5.8.1 +- CocoaPods 1.12.1 以降 +- WebRTC SFU Sora 2023.1.0 以降 Xcode と Swift のバージョンによっては、 CocoaPods で取得できるバイナリに互換性がない可能性があります。詳しくはドキュメントを参照してください。 diff --git a/Sora.podspec b/Sora.podspec index 381c00cd..27725513 100644 --- a/Sora.podspec +++ b/Sora.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "Sora" - s.version = "2023.1.0" + s.version = "2023.2.0" s.summary = "Sora iOS SDK" s.description = <<-DESC A library to develop Sora client applications. @@ -15,7 +15,7 @@ Pod::Spec.new do |s| } s.source_files = "Sora/**/*.swift" s.resources = ['Sora/*.xib'] - s.dependency "WebRTC", '112.5615.1.0' + s.dependency "WebRTC", '115.5790.7.0' s.pod_target_xcconfig = { 'ARCHS' => 'arm64', 'ARCHS[config=Debug]' => '$(ARCHS_STANDARD)' diff --git a/Sora.xcodeproj/project.pbxproj b/Sora.xcodeproj/project.pbxproj index 537cfea4..c4b31ec9 100644 --- a/Sora.xcodeproj/project.pbxproj +++ b/Sora.xcodeproj/project.pbxproj @@ -594,7 +594,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 2023.1.0; + MARKETING_VERSION = 2023.2.0; PRODUCT_BUNDLE_IDENTIFIER = jp.shiguredo.sora.ios.sdk.Sora; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; @@ -604,6 +604,7 @@ SWIFT_SWIFT3_OBJC_INFERENCE = Off; SWIFT_VERSION = 5.0; VALID_ARCHS = arm64; + WATCHOS_DEPLOYMENT_TARGET = ""; }; name = Debug; }; @@ -627,7 +628,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 2023.1.0; + MARKETING_VERSION = 2023.2.0; PRODUCT_BUNDLE_IDENTIFIER = jp.shiguredo.sora.ios.sdk.Sora; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; @@ -636,6 +637,7 @@ SWIFT_SWIFT3_OBJC_INFERENCE = Off; SWIFT_VERSION = 5.0; VALID_ARCHS = arm64; + WATCHOS_DEPLOYMENT_TARGET = ""; }; name = Release; }; @@ -657,6 +659,7 @@ PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_SWIFT3_OBJC_INFERENCE = Default; SWIFT_VERSION = 5.0; + WATCHOS_DEPLOYMENT_TARGET = ""; }; name = Debug; }; @@ -678,6 +681,7 @@ PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_SWIFT3_OBJC_INFERENCE = Default; SWIFT_VERSION = 5.0; + WATCHOS_DEPLOYMENT_TARGET = ""; }; name = Release; }; diff --git a/Sora/CameraVideoCapturer.swift b/Sora/CameraVideoCapturer.swift index c3db782f..6ff2b791 100644 --- a/Sora/CameraVideoCapturer.swift +++ b/Sora/CameraVideoCapturer.swift @@ -237,12 +237,12 @@ public final class CameraVideoCapturer { /// 停止前と同じ設定でカメラを再起動します。 public func restart(completionHandler: @escaping ((Error?) -> Void)) { - guard let format = format else { + guard let format else { completionHandler(SoraError.cameraError(reason: "failed to access format")) return } - guard let frameRate = frameRate else { + guard let frameRate else { completionHandler(SoraError.cameraError(reason: "failed to access frame rate")) return } @@ -255,7 +255,8 @@ public final class CameraVideoCapturer { } start(format: format, - frameRate: frameRate) { (error: Error?) in + frameRate: frameRate) + { (error: Error?) in guard error == nil else { completionHandler(error) return @@ -267,7 +268,8 @@ public final class CameraVideoCapturer { } } else { start(format: format, - frameRate: frameRate) { (error: Error?) in + frameRate: frameRate) + { (error: Error?) in guard error == nil else { completionHandler(error) return diff --git a/Sora/Configuration.swift b/Sora/Configuration.swift index 08c23744..baa4c002 100644 --- a/Sora/Configuration.swift +++ b/Sora/Configuration.swift @@ -43,7 +43,7 @@ public struct Proxy: CustomStringConvertible { self.username = username self.password = password - if let agent = agent { + if let agent { self.agent = agent } } @@ -216,6 +216,18 @@ public struct Configuration { /// プロキシに関する設定 public var proxy: Proxy? + /// 転送フィルターの設定 + public var forwardingFilter: ForwardingFilter? + + /// VP9 向け映像コーデックパラメーター + public var videoVp9Params: Encodable? + + /// AV1 向け映像コーデックパラメーター + public var videoAv1Params: Encodable? + + /// H264 向け映像コーデックパラメーター + public var videoH264Params: Encodable? + // MARK: - イベントハンドラ /// WebSocket チャネルに関するイベントハンドラ @@ -334,3 +346,91 @@ public struct Configuration { self.multistreamEnabled = multistreamEnabled } } + +/** + 転送フィルターのルールのフィールドの設定です。 + */ +public enum ForwardingFilterRuleField: String, Codable { + /// connection_id + case connectionId = "connection_id" + + /// client_id + case clientId = "client_id" + + /// kind + case kind +} + +/** + 転送フィルターのルールの演算子の設定です。 + */ +public enum ForwardingFilterRuleOperator: String, Codable { + /// is_in + case isIn = "is_in" + + /// is_not_in + case isNotIn = "is_not_in" +} + +/** + 転送フィルターのルールの設定です。 + */ +public struct ForwardingFilterRule: Codable { + /// field + public let field: ForwardingFilterRuleField + + /// operator + public let `operator`: ForwardingFilterRuleOperator + + /// values + public let values: [String] + + /** + 初期化します。 + + - parameter field: field + - parameter operator: operator + - parameter values: values + */ + public init(field: ForwardingFilterRuleField, + operator: ForwardingFilterRuleOperator, + values: [String]) + { + self.field = field + self.operator = `operator` + self.values = values + } +} + +/** + 転送フィルターのアクションの設定です。 + */ +public enum ForwardingFilterAction: String, Codable { + /// block + case block + + /// allow + case allow +} + +/** + 転送フィルターに関する設定です。 + */ +public struct ForwardingFilter: Codable { + /// action + public let action: ForwardingFilterAction + + /// rules + public let rules: [[ForwardingFilterRule]] + + /** + 初期化します。 + + - parameter action: action + - parameter rules: rules + */ + public init(action: ForwardingFilterAction, rules: [[ForwardingFilterRule]]) { + self.action = action + self.rules = rules + } +} diff --git a/Sora/ConnectionTimer.swift b/Sora/ConnectionTimer.swift index 568421b7..63518394 100644 --- a/Sora/ConnectionTimer.swift +++ b/Sora/ConnectionTimer.swift @@ -39,7 +39,7 @@ class ConnectionTimer { } public func run(timeout: Int? = nil, handler: @escaping () -> Void) { - if let timeout = timeout { + if let timeout { self.timeout = timeout } Logger.debug(type: .connectionTimer, diff --git a/Sora/DataChannel.swift b/Sora/DataChannel.swift index 24e81ae4..e981e56b 100644 --- a/Sora/DataChannel.swift +++ b/Sora/DataChannel.swift @@ -121,7 +121,7 @@ class BasicDataChannelDelegate: NSObject, RTCDataChannelDelegate { Logger.debug(type: .dataChannel, message: "\(#function): label => \(dataChannel.label), state => \(dataChannel.readyState)") if dataChannel.readyState == .closed { - if let peerChannel = peerChannel { + if let peerChannel { // DataChannel が切断されたタイミングで PeerChannel を切断する // PeerChannel -> DataChannel の順に切断されるパターンも存在するが、 // PeerChannel.disconnect(error:reason:) 側で排他処理が実装されているため問題ない @@ -137,7 +137,7 @@ class BasicDataChannelDelegate: NSObject, RTCDataChannelDelegate { func dataChannel(_ dataChannel: RTCDataChannel, didReceiveMessageWith buffer: RTCDataBuffer) { Logger.debug(type: .dataChannel, message: "\(#function): label => \(dataChannel.label)") - guard let peerChannel = peerChannel else { + guard let peerChannel else { Logger.error(type: .dataChannel, message: "peerChannel is unavailable") return } @@ -173,7 +173,7 @@ class BasicDataChannelDelegate: NSObject, RTCDataChannelDelegate { Logger.error(type: .dataChannel, message: "failed to encode stats data to json") } - if let data = data { + if let data { let ok = dc.send(data) if !ok { Logger.error(type: .dataChannel, message: "failed to send stats data over DataChannel") @@ -195,7 +195,7 @@ class BasicDataChannelDelegate: NSObject, RTCDataChannelDelegate { Logger.error(type: .dataChannel, message: "unknown data channel label: \(dataChannel.label)") } } - if let mediaChannel = mediaChannel, let handler = mediaChannel.handlers.onDataChannelMessage { + if let mediaChannel, let handler = mediaChannel.handlers.onDataChannelMessage { handler(mediaChannel, dataChannel.label, data) } } diff --git a/Sora/ICEServerInfo.swift b/Sora/ICEServerInfo.swift index 9cf1861c..06040d6f 100644 --- a/Sora/ICEServerInfo.swift +++ b/Sora/ICEServerInfo.swift @@ -72,10 +72,10 @@ extension ICEServerInfo: Codable { public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(urls, forKey: .urls) - if let userName = userName { + if let userName { try container.encode(userName, forKey: .userName) } - if let credential = credential { + if let credential { try container.encode(credential, forKey: .credential) } } diff --git a/Sora/MediaChannel.swift b/Sora/MediaChannel.swift index ac324aae..66f76dcb 100644 --- a/Sora/MediaChannel.swift +++ b/Sora/MediaChannel.swift @@ -384,7 +384,7 @@ public final class MediaChannel { weakSelf.connectionTimer.stop() connectionTask.complete() - if let error = error { + if let error { Logger.error(type: .mediaChannel, message: "failed to connect") weakSelf.internalDisconnect(error: error, reason: .signalingFailure) handler(error) @@ -426,7 +426,7 @@ public final class MediaChannel { default: Logger.debug(type: .mediaChannel, message: "try disconnecting") - if let error = error { + if let error { Logger.error(type: .mediaChannel, message: "error: \(error.localizedDescription)") } diff --git a/Sora/MediaStream.swift b/Sora/MediaStream.swift index 3d2ba0db..4ac0d1a2 100644 --- a/Sora/MediaStream.swift +++ b/Sora/MediaStream.swift @@ -220,7 +220,7 @@ class BasicMediaStream: MediaStream { nativeAudioTrack?.source.volume } set { - guard let newValue = newValue else { + guard let newValue else { return } if let track = nativeAudioTrack { diff --git a/Sora/NativePeerChannelFactory.swift b/Sora/NativePeerChannelFactory.swift index de583b08..953a7fe1 100644 --- a/Sora/NativePeerChannelFactory.swift +++ b/Sora/NativePeerChannelFactory.swift @@ -59,7 +59,7 @@ class NativePeerChannelFactory { proxy: Proxy? = nil, delegate: RTCPeerConnectionDelegate?) -> RTCPeerConnection? { - if let proxy = proxy { + if let proxy { return nativeFactory.peerConnection(with: configuration.nativeValue, constraints: constraints.nativeValue, certificateVerifier: nil, diff --git a/Sora/PackageInfo.swift b/Sora/PackageInfo.swift index 261f2a72..61b35db2 100644 --- a/Sora/PackageInfo.swift +++ b/Sora/PackageInfo.swift @@ -1,7 +1,7 @@ /// :nodoc: public enum SDKInfo { // Sora iOS SDK のバージョンを定義する - public static let version = "2023.1.0" + public static let version = "2023.2.0" } /** @@ -9,16 +9,16 @@ public enum SDKInfo { */ public enum WebRTCInfo { /// WebRTC フレームワークのバージョン - public static let version = "M112" + public static let version = "M115" /// WebRTC フレームワークのコミットポジション - public static let commitPosition = "1" + public static let commitPosition = "7" /// WebRTC フレームワークのメンテナンスバージョン public static let maintenanceVersion = "0" /// WebRTC フレームワークのソースコードのリビジョン - public static let revision = "18a31880e3037f9938581c1210d2d73b9685f4aa" + public static let revision = "2abe6e2214fa4fcecdb9614715c55a82c0067e25" /// WebRTC フレームワークのソースコードのリビジョン (短縮版) public static var shortRevision: String { diff --git a/Sora/PeerChannel.swift b/Sora/PeerChannel.swift index 2caebe46..e7cbb615 100644 --- a/Sora/PeerChannel.swift +++ b/Sora/PeerChannel.swift @@ -72,7 +72,7 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate { switch shouldDisconnect { case (true, let error, let reason): shouldDisconnect = (false, nil, .unknown) - if let context = context { + if let context { if context.state != .closed { context.basicDisconnect(error: error, reason: reason) } @@ -99,7 +99,7 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate { weak var mediaChannel: MediaChannel? var state: PeerChannelConnectionState { - guard let nativeChannel = nativeChannel else { + guard let nativeChannel else { return PeerChannelConnectionState(RTCPeerConnectionState.new) } @@ -198,7 +198,7 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate { func remove(streamId: String) { let stream = streams.first { stream in stream.streamId == streamId } - if let stream = stream { + if let stream { remove(stream: stream) } } @@ -230,7 +230,7 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate { // MARK: - Private methods private func sendConnectMessage(error: Error?) { - if let error = error { + if let error { Logger.error(type: .peerChannel, message: "failed connecting to signaling channel (\(error.localizedDescription))") onConnectHandler?(error) @@ -318,7 +318,11 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate { dataChannelSignaling: configuration.dataChannelSignaling, ignoreDisconnectWebSocket: configuration.ignoreDisconnectWebSocket, audioStreamingLanguageCode: configuration.audioStreamingLanguageCode, - redirect: redirect + redirect: redirect, + forwardingFilter: configuration.forwardingFilter, + vp9Params: configuration.videoVp9Params, + av1Params: configuration.videoAv1Params, + h264Params: configuration.videoH264Params ) Logger.debug(type: .peerChannel, message: "send connect") @@ -326,7 +330,7 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate { } private func initializeSenderStream(mid: [String: String]? = nil) { - guard let nativeChannel = nativeChannel else { + guard let nativeChannel else { Logger.debug(type: .peerChannel, message: "nativeChannel shoud not be nil") return } @@ -344,7 +348,7 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate { let stream = BasicMediaStream(peerChannel: self, nativeStream: nativeStream) - if let mid = mid { + if let mid { Logger.info(type: .peerChannel, message: "mid => \(mid)") if let audioMid = mid["audio"] { guard let audioTransceiver = (nativeChannel.transceivers.first { $0.mid == audioMid }) else { @@ -427,7 +431,7 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate { AVAudioSession.Category.playAndRecord.rawValue RTCAudioSession.sharedInstance().initializeInput { error in - if let error = error { + if let error { Logger.debug(type: .peerChannel, message: "failed to initialize audio input => \(error.localizedDescription)") return @@ -538,7 +542,7 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate { mid: [String: String]? = nil, handler: @escaping (String?, Error?) -> Void) { - guard let nativeChannel = nativeChannel else { + guard let nativeChannel else { Logger.debug(type: .peerChannel, message: "nativeChannel shoud not be nil") return } @@ -567,9 +571,8 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate { if isSender { if initialOffer { self.initializeSenderStream(mid: mid) - } else { - self.updateSenderOfferEncodings() } + self.updateSenderOfferEncodings() } Logger.debug(type: .peerChannel, message: "try creating native answer") @@ -609,7 +612,7 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate { } private func updateSenderOfferEncodings() { - guard let nativeChannel = nativeChannel else { + guard let nativeChannel else { Logger.debug(type: .peerChannel, message: "nativeChannel shoud not be nil") return } @@ -625,7 +628,7 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate { } private func createAndSendAnswer(offer: SignalingOffer) { - guard let nativeChannel = nativeChannel else { + guard let nativeChannel else { Logger.debug(type: .peerChannel, message: "nativeChannel shoud not be nil") return } @@ -647,7 +650,8 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate { offer: offer.sdp, constraints: webRTCConfiguration.nativeConstraints, initialOffer: true, - mid: offer.mid) { sdp, error in + mid: offer.mid) + { sdp, error in guard error == nil else { Logger.error(type: .peerChannel, message: "failed to create answer (\(error!.localizedDescription))") @@ -669,7 +673,8 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate { lock.lock() createAnswer(isSender: false, offer: offer, - constraints: webRTCConfiguration.nativeConstraints) { answer, error in + constraints: webRTCConfiguration.nativeConstraints) + { answer, error in guard error == nil else { Logger.error(type: .peerChannel, message: "failed to create update-answer (\(error!.localizedDescription)") @@ -698,7 +703,8 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate { lock.lock() createAnswer(isSender: false, offer: reOffer, - constraints: webRTCConfiguration.nativeConstraints) { answer, error in + constraints: webRTCConfiguration.nativeConstraints) + { answer, error in guard error == nil else { Logger.error(type: .peerChannel, message: "failed to create re-answer (\(error!.localizedDescription)") @@ -732,7 +738,8 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate { lock.lock() createAnswer(isSender: false, offer: reOffer, - constraints: webRTCConfiguration.nativeConstraints) { answer, error in + constraints: webRTCConfiguration.nativeConstraints) + { answer, error in guard error == nil else { Logger.error(type: .peerChannel, message: "failed to create re-answer: error => (\(error!.localizedDescription)") @@ -756,7 +763,7 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate { return } - if let data = data { + if let data { let ok = dataChannel.send(data) if !ok { Logger.error(type: .peerChannel, @@ -831,7 +838,7 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate { } } - if let mediaChannel = mediaChannel, let onDataChannel = mediaChannel.handlers.onDataChannel { + if let mediaChannel, let onDataChannel = mediaChannel.handlers.onDataChannel { onDataChannel(mediaChannel) } case let .redirect(redirect): @@ -879,7 +886,7 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate { private func basicDisconnect(error: Error?, reason: DisconnectReason) { Logger.debug(type: .peerChannel, message: "try disconnecting: error => \(String(describing: error != nil ? error?.localizedDescription : "nil")), reason => \(reason)") - if let error = error { + if let error { Logger.error(type: .peerChannel, message: "error: \(error.localizedDescription)") } @@ -985,7 +992,7 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate { message: "failed to encode \(message.typeName()) message to json: error => (\(error.localizedDescription)") } - if let data = data { + if let data { let ok = dataChannel.send(data) if !ok { Logger.error(type: .peerChannel, message: "failed to send \(message.typeName()) message over DataChannel") @@ -1123,7 +1130,7 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate { }.first ?? nil let compress = dataChannelSetting?["compress"] as? Bool ?? false - guard let mediaChannel = mediaChannel else { + guard let mediaChannel else { Logger.warn(type: .peerChannel, message: "mediaChannel is unavailable") return } @@ -1161,15 +1168,20 @@ extension RTCRtpSender { } if let value = encoding.maxBitrate { - Logger.debug(type: .peerChannel, message: "maxBitrate: \(value))") + Logger.debug(type: .peerChannel, message: "maxBitrate: \(value)") oldEncoding.maxBitrateBps = NSNumber(value: value) } if let value = encoding.scaleResolutionDownBy { - Logger.debug(type: .peerChannel, message: "scaleResolutionDownBy: \(value))") + Logger.debug(type: .peerChannel, message: "scaleResolutionDownBy: \(value)") oldEncoding.scaleResolutionDownBy = NSNumber(value: value) } + if let value = encoding.scalabilityMode { + Logger.debug(type: .peerChannel, message: "scalabilityMode: \(value)") + oldEncoding.scalabilityMode = value + } + break } } diff --git a/Sora/Signaling.swift b/Sora/Signaling.swift index decddcbc..33367118 100644 --- a/Sora/Signaling.swift +++ b/Sora/Signaling.swift @@ -369,6 +369,18 @@ public struct SignalingConnect { /// type: redicret 受信後の再接続 public var redirect: Bool? + + /// 転送フィルターの設定 + public var forwardingFilter: ForwardingFilter? + + /// VP9 向け映像コーデックパラメーター + public var vp9Params: Encodable? + + /// AV1 向け映像コーデックパラメーター + public var av1Params: Encodable? + + /// H264 向け映像コーデックパラメーター + public var h264Params: Encodable? } /** @@ -409,6 +421,9 @@ public struct SignalingOffer { /// 映像解像度を送信前に下げる度合 public let scaleResolutionDownBy: Double? + /// scalability mode + public let scalabilityMode: String? + /// RTP エンコーディングに関するパラメーター public var rtpEncodingParameters: RTCRtpEncodingParameters { let params = RTCRtpEncodingParameters() @@ -422,6 +437,7 @@ public struct SignalingOffer { if let value = scaleResolutionDownBy { params.scaleResolutionDownBy = NSNumber(value: value) } + params.scalabilityMode = scalabilityMode return params } } @@ -703,21 +719,21 @@ extension Signaling: Codable { let type = try container.decode(String.self, forKey: .type) switch type { case "offer": - self = .offer(try SignalingOffer(from: decoder)) + self = try .offer(SignalingOffer(from: decoder)) case "update": - self = .update(try SignalingUpdate(from: decoder)) + self = try .update(SignalingUpdate(from: decoder)) case "notify": - self = .notify(try SignalingNotify(from: decoder)) + self = try .notify(SignalingNotify(from: decoder)) case "ping": - self = .ping(try SignalingPing(from: decoder)) + self = try .ping(SignalingPing(from: decoder)) case "push": - self = .push(try SignalingPush(from: decoder)) + self = try .push(SignalingPush(from: decoder)) case "re-offer": - self = .reOffer(try SignalingReOffer(from: decoder)) + self = try .reOffer(SignalingReOffer(from: decoder)) case "switched": - self = .switched(try SignalingSwitched(from: decoder)) + self = try .switched(SignalingSwitched(from: decoder)) case "redirect": - self = .redirect(try SignalingRedirect(from: decoder)) + self = try .redirect(SignalingRedirect(from: decoder)) default: throw SoraError.unknownSignalingMessageType(type: type) } @@ -838,11 +854,15 @@ extension SignalingConnect: Codable { case data_channels case audio_streaming_language_code case redirect + case forwarding_filter } enum VideoCodingKeys: String, CodingKey { case codec_type case bit_rate + case vp9_params + case av1_params + case h264_params } enum AudioCodingKeys: String, CodingKey { @@ -874,9 +894,10 @@ extension SignalingConnect: Codable { try container.encodeIfPresent(ignoreDisconnectWebSocket, forKey: .ignore_disconnect_websocket) try container.encodeIfPresent(audioStreamingLanguageCode, forKey: .audio_streaming_language_code) try container.encodeIfPresent(redirect, forKey: .redirect) + try container.encodeIfPresent(forwardingFilter, forKey: .forwarding_filter) if videoEnabled { - if videoCodec != .default || videoBitRate != nil { + if videoCodec != .default || videoBitRate != nil || vp9Params != nil || av1Params != nil || h264Params != nil { var videoContainer = container .nestedContainer(keyedBy: VideoCodingKeys.self, forKey: .video) @@ -885,6 +906,18 @@ extension SignalingConnect: Codable { } try videoContainer.encodeIfPresent(videoBitRate, forKey: .bit_rate) + if let vp9Params { + let vp9ParamsEnc = videoContainer.superEncoder(forKey: .vp9_params) + try vp9Params.encode(to: vp9ParamsEnc) + } + if let av1Params { + let av1ParamsEnc = videoContainer.superEncoder(forKey: .av1_params) + try av1Params.encode(to: av1ParamsEnc) + } + if let h264Params { + let h264ParamsEnc = videoContainer.superEncoder(forKey: .h264_params) + try h264Params.encode(to: h264ParamsEnc) + } } } else { try container.encode(false, forKey: .video) @@ -959,6 +992,7 @@ extension SignalingOffer.Encoding: Codable { case maxBitrate case maxFramerate case scaleResolutionDownBy + case scalabilityMode } public init(from decoder: Decoder) throws { @@ -969,6 +1003,8 @@ extension SignalingOffer.Encoding: Codable { maxFramerate = try container.decodeIfPresent(Double.self, forKey: .maxFramerate) scaleResolutionDownBy = try container.decodeIfPresent(Double.self, forKey: .scaleResolutionDownBy) + scalabilityMode = try container.decodeIfPresent(String.self, + forKey: .scalabilityMode) } public func encode(to encoder: Encoder) throws { diff --git a/Sora/SignalingChannel.swift b/Sora/SignalingChannel.swift index 964bfab3..44f7df84 100644 --- a/Sora/SignalingChannel.swift +++ b/Sora/SignalingChannel.swift @@ -243,7 +243,7 @@ class SignalingChannel { break default: Logger.debug(type: .signalingChannel, message: "try disconnecting") - if let error = error { + if let error { Logger.error(type: .signalingChannel, message: "error: \(error.localizedDescription)") } @@ -279,7 +279,7 @@ class SignalingChannel { switch message { case .connect: if configuration.dataChannels != nil { - var jsonObject = (try JSONSerialization.jsonObject(with: data, options: [])) as! [String: Any] + var jsonObject = try (JSONSerialization.jsonObject(with: data, options: [])) as! [String: Any] jsonObject["data_channels"] = configuration.dataChannels data = try JSONSerialization.data(withJSONObject: jsonObject, options: []) } diff --git a/Sora/Sora.swift b/Sora/Sora.swift index 2360735f..e3f05eee 100644 --- a/Sora/Sora.swift +++ b/Sora/Sora.swift @@ -173,7 +173,7 @@ public final class Sora { guard let weakSelf = self else { return } - guard let mediaChan = mediaChan else { + guard let mediaChan else { return } weakSelf.remove(mediaChannel: mediaChan) @@ -189,11 +189,11 @@ public final class Sora { guard let weakSelf = self else { return } - guard let mediaChan = mediaChan else { + guard let mediaChan else { return } - if let error = error { + if let error { handler(nil, error) weakSelf.handlers.onConnect?(nil, error) return diff --git a/Sora/SoraError.swift b/Sora/SoraError.swift index f6bd0a3b..dcc03a33 100644 --- a/Sora/SoraError.swift +++ b/Sora/SoraError.swift @@ -51,7 +51,7 @@ extension SoraError: LocalizedError { return "Connection is busy (\(reason))" case let .webSocketClosed(statusCode: statusCode, reason: reason): var desc = "WebSocket is closed (\(statusCode.intValue()) " - if let reason = reason { + if let reason { desc.append(reason) } else { desc.append("Unknown reason") diff --git a/Sora/URLSessionWebSocketChannel.swift b/Sora/URLSessionWebSocketChannel.swift index 5cd2f5cd..de5eb9dd 100644 --- a/Sora/URLSessionWebSocketChannel.swift +++ b/Sora/URLSessionWebSocketChannel.swift @@ -26,7 +26,7 @@ class URLSessionWebSocketChannel: NSObject, URLSessionDelegate, URLSessionTaskDe func connect(delegateQueue: OperationQueue?) { let configuration = URLSessionConfiguration.ephemeral - if let proxy = proxy { + if let proxy { configuration.connectionProxyDictionary = [ kCFNetworkProxiesHTTPProxy: proxy.host, kCFNetworkProxiesHTTPPort: proxy.port, @@ -70,7 +70,7 @@ class URLSessionWebSocketChannel: NSObject, URLSessionDelegate, URLSessionTaskDe isClosing = true Logger.debug(type: .webSocketChannel, message: "[\(host)] disconnecting") - if let error = error { + if let error { Logger.debug(type: .webSocketChannel, message: "[\(host)] error: \(error.localizedDescription)") internalHandlers.onDisconnectWithError?(self, error) @@ -105,7 +105,7 @@ class URLSessionWebSocketChannel: NSObject, URLSessionDelegate, URLSessionTaskDe return } - if let error = error { + if let error { Logger.debug(type: .webSocketChannel, message: "[\(weakSelf.host)] failed to send message") weakSelf.disconnect(error: error) } @@ -171,7 +171,7 @@ class URLSessionWebSocketChannel: NSObject, URLSessionDelegate, URLSessionTaskDe } func reason2string(reason: Data?) -> String? { - guard let reason = reason else { + guard let reason else { return nil } diff --git a/Sora/Utilities.swift b/Sora/Utilities.swift index 87fa022a..ce462a7f 100644 --- a/Sora/Utilities.swift +++ b/Sora/Utilities.swift @@ -11,7 +11,7 @@ public enum Utilities { var chars: [String] = [] chars.reserveCapacity(length) for _ in 0 ..< length { - let index = arc4random_uniform(UInt32(Utilities.randomBaseChars.count)) + let index = UInt32.random(in: 0 ..< UInt32(Utilities.randomBaseChars.count)) chars.append(randomBaseChars[Int(index)]) } return chars.joined() diff --git a/Sora/VideoRenderer.swift b/Sora/VideoRenderer.swift index 1f8300e1..d61a3396 100644 --- a/Sora/VideoRenderer.swift +++ b/Sora/VideoRenderer.swift @@ -78,7 +78,7 @@ class VideoRendererAdapter: NSObject, RTCVideoRenderer { func renderFrame(_ frame: RTCVideoFrame?) { DispatchQueue.main.async { if let renderer = self.videoRenderer { - if let frame = frame { + if let frame { let frame = VideoFrame.native(capturer: nil, frame: frame) renderer.render(videoFrame: frame) } else {