From 8b73c31faea543fa3fcd62be244a98455c8ba0af Mon Sep 17 00:00:00 2001 From: zztkm Date: Thu, 12 Sep 2024 18:44:35 +0900 Subject: [PATCH 1/4] =?UTF-8?q?Answer=20=E3=81=AE=20SDP=20=E3=82=92?= =?UTF-8?q?=E6=9B=B8=E3=81=8D=E6=8F=9B=E3=81=88=E3=82=8B=E5=87=A6=E7=90=86?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sora/Configuration.swift | 3 +++ Sora/PeerChannel.swift | 32 ++++++++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/Sora/Configuration.swift b/Sora/Configuration.swift index 72e8c426..fa4c9d99 100644 --- a/Sora/Configuration.swift +++ b/Sora/Configuration.swift @@ -215,6 +215,9 @@ public struct Configuration { /// 通常、指定する必要はありません。 public var publisherAudioTrackId: String = defaultPublisherAudioTrackId + /// ステレオ出力を許可するための設定です。 + public var forceStereoOutput: Bool = false + /** 初期化します。 diff --git a/Sora/PeerChannel.swift b/Sora/PeerChannel.swift index 6c33f7f6..a912a2f4 100644 --- a/Sora/PeerChannel.swift +++ b/Sora/PeerChannel.swift @@ -577,7 +577,6 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate { Logger.debug(type: .peerChannel, message: "try create answer") Logger.debug(type: .peerChannel, message: offer) - Logger.debug(type: .peerChannel, message: "try setting remote description") let offer = RTCSessionDescription(type: .offer, sdp: offer) nativeChannel.setRemoteDescription(offer) { error in @@ -689,7 +688,36 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate { return } - let answer = SignalingAnswer(sdp: sdp!) + var sdp = sdp! + + // Chrome/Edge 向けのハック stereo=1 が CreateOffer では付与されないので、 + // SDP を書き換えて stereo=1 を付与する + // https://github.com/w3c/webrtc-stats/issues/686 + // https://github.com/w3c/webrtc-extensions/issues/63 + // https://issues.webrtc.org/issues/41481053#comment18 + if (self.configuration.forceStereoOutput) { + Logger.debug(type: .peerChannel, message: "stereo=1 を付与する") + let pattern = "minptime=\\d+" + let regexp = try! NSRegularExpression(pattern: pattern) + let replacementFunc: (String) -> String = { match in + if !match.contains("stereo=1") { + return "\(match);stereo=1" + } + return match + } + + let matches = regexp.matches(in: sdp, range: NSRange(sdp.startIndex..., in: sdp)) + for match in matches.reversed() { + print("kensaku: match: \(match)") + let range = match.range + let matchedString = (sdp as NSString).substring(with: range) + let replacedString = replacementFunc(matchedString) + print("kensaku: replacedString: \(replacedString)") + sdp = (sdp as NSString).replacingCharacters(in: range, with: replacedString) + } + } + + let answer = SignalingAnswer(sdp: sdp) self.signalingChannel.send(message: Signaling.answer(answer)) self.lock.unlock() Logger.debug(type: .peerChannel, message: "did send answer") From e18849a56d86eecd91c6fee9d9c9e676237ca2da Mon Sep 17 00:00:00 2001 From: zztkm Date: Thu, 12 Sep 2024 22:01:08 +0900 Subject: [PATCH 2/4] =?UTF-8?q?answer.sdp=20=E3=82=92=E6=9B=B8=E3=81=8D?= =?UTF-8?q?=E6=8F=9B=E3=81=88=E3=80=81=E3=81=9D=E3=81=AE=E5=80=A4=E3=81=A7?= =?UTF-8?q?=20local=20description=20=E3=82=92=E8=A8=AD=E5=AE=9A=E3=81=99?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sora/PeerChannel.swift | 65 +++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/Sora/PeerChannel.swift b/Sora/PeerChannel.swift index a912a2f4..0bb53ebc 100644 --- a/Sora/PeerChannel.swift +++ b/Sora/PeerChannel.swift @@ -616,10 +616,38 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate { return } + // answer sdp を加工する + var sdp = answer!.sdp + // TODO(zztkm): この実装だと stereo=1 がすでに付与されていても付与してしまうので修正する + // Chrome/Edge 向けのハック stereo=1 が CreateOffer では付与されないので、 + // SDP を書き換えて stereo=1 を付与する + // https://github.com/w3c/webrtc-stats/issues/686 + // https://github.com/w3c/webrtc-extensions/issues/63 + // https://issues.webrtc.org/issues/41481053#comment18 + if self.configuration.forceStereoOutput { + Logger.debug(type: .peerChannel, message: "stereo=1 を付与する") + let pattern = "minptime=\\d+" + let regexp = try! NSRegularExpression(pattern: pattern) + let replacementFunc: (String) -> String = { match in + if !match.contains("stereo=1") { + return "\(match);stereo=1" + } + return match + } + + let matches = regexp.matches(in: sdp, range: NSRange(sdp.startIndex..., in: sdp)) + for match in matches.reversed() { + let range = match.range + let matchedString = (sdp as NSString).substring(with: range) + let replacedString = replacementFunc(matchedString) + sdp = (sdp as NSString).replacingCharacters(in: range, with: replacedString) + } + } + let updatedAnswer = RTCSessionDescription(type: .answer, sdp: sdp) Logger.debug(type: .peerChannel, message: "did create answer") Logger.debug(type: .peerChannel, message: "try setting local description") - nativeChannel.setLocalDescription(answer!) { error in + nativeChannel.setLocalDescription(updatedAnswer) { error in guard error == nil else { Logger.debug(type: .peerChannel, message: "failed setting local description") @@ -629,10 +657,10 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate { Logger.debug(type: .peerChannel, message: "did set local description") Logger.debug(type: .peerChannel, - message: "\(answer!.sdpDescription)") + message: "\(updatedAnswer.sdpDescription)") Logger.debug(type: .peerChannel, message: "did create answer") - handler(answer!.sdp, nil) + handler(updatedAnswer.sdp, nil) } } } @@ -688,36 +716,7 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate { return } - var sdp = sdp! - - // Chrome/Edge 向けのハック stereo=1 が CreateOffer では付与されないので、 - // SDP を書き換えて stereo=1 を付与する - // https://github.com/w3c/webrtc-stats/issues/686 - // https://github.com/w3c/webrtc-extensions/issues/63 - // https://issues.webrtc.org/issues/41481053#comment18 - if (self.configuration.forceStereoOutput) { - Logger.debug(type: .peerChannel, message: "stereo=1 を付与する") - let pattern = "minptime=\\d+" - let regexp = try! NSRegularExpression(pattern: pattern) - let replacementFunc: (String) -> String = { match in - if !match.contains("stereo=1") { - return "\(match);stereo=1" - } - return match - } - - let matches = regexp.matches(in: sdp, range: NSRange(sdp.startIndex..., in: sdp)) - for match in matches.reversed() { - print("kensaku: match: \(match)") - let range = match.range - let matchedString = (sdp as NSString).substring(with: range) - let replacedString = replacementFunc(matchedString) - print("kensaku: replacedString: \(replacedString)") - sdp = (sdp as NSString).replacingCharacters(in: range, with: replacedString) - } - } - - let answer = SignalingAnswer(sdp: sdp) + let answer = SignalingAnswer(sdp: sdp!) self.signalingChannel.send(message: Signaling.answer(answer)) self.lock.unlock() Logger.debug(type: .peerChannel, message: "did send answer") From 10719373aa9fb899e391449d91fbdc3da14ba31a Mon Sep 17 00:00:00 2001 From: zztkm Date: Fri, 13 Sep 2024 16:10:12 +0900 Subject: [PATCH 3/4] =?UTF-8?q?=E9=80=94=E4=B8=AD=E7=B5=8C=E9=81=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sora/PeerChannel.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sora/PeerChannel.swift b/Sora/PeerChannel.swift index 0bb53ebc..73d0a7fd 100644 --- a/Sora/PeerChannel.swift +++ b/Sora/PeerChannel.swift @@ -626,9 +626,10 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate { // https://issues.webrtc.org/issues/41481053#comment18 if self.configuration.forceStereoOutput { Logger.debug(type: .peerChannel, message: "stereo=1 を付与する") - let pattern = "minptime=\\d+" + let pattern = "minptime=\\d+(?!;stereo=1)" let regexp = try! NSRegularExpression(pattern: pattern) let replacementFunc: (String) -> String = { match in + print("kensaku: match str: \(match)") if !match.contains("stereo=1") { return "\(match);stereo=1" } From d15ce309b4ae6cd0cfa4772615f993792e1ad9a7 Mon Sep 17 00:00:00 2001 From: zztkm Date: Fri, 13 Sep 2024 18:56:29 +0900 Subject: [PATCH 4/4] =?UTF-8?q?=E6=AD=A3=E8=A6=8F=E8=A1=A8=E7=8F=BE?= =?UTF-8?q?=E3=81=AE=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sora/PeerChannel.swift | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/Sora/PeerChannel.swift b/Sora/PeerChannel.swift index 73d0a7fd..a4d7c2f2 100644 --- a/Sora/PeerChannel.swift +++ b/Sora/PeerChannel.swift @@ -626,23 +626,12 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate { // https://issues.webrtc.org/issues/41481053#comment18 if self.configuration.forceStereoOutput { Logger.debug(type: .peerChannel, message: "stereo=1 を付与する") - let pattern = "minptime=\\d+(?!;stereo=1)" + let pattern = "(\\bminptime=\\d+\\b(?!;stereo=1))" + let replacementString = "$1;stereo=1" let regexp = try! NSRegularExpression(pattern: pattern) - let replacementFunc: (String) -> String = { match in - print("kensaku: match str: \(match)") - if !match.contains("stereo=1") { - return "\(match);stereo=1" - } - return match - } - - let matches = regexp.matches(in: sdp, range: NSRange(sdp.startIndex..., in: sdp)) - for match in matches.reversed() { - let range = match.range - let matchedString = (sdp as NSString).substring(with: range) - let replacedString = replacementFunc(matchedString) - sdp = (sdp as NSString).replacingCharacters(in: range, with: replacedString) - } + // NSRegularExpression が NSString に基づく API で、NSString は UTF-16 エンコーディングを + // 使用するので、 length を utf16.count で取得している + sdp = regexp.stringByReplacingMatches(in: sdp, range: NSRange(location: 0, length: sdp.utf16.count), withTemplate: replacementString) } let updatedAnswer = RTCSessionDescription(type: .answer, sdp: sdp) Logger.debug(type: .peerChannel, message: "did create answer")