Skip to content

Commit 77fd53d

Browse files
authored
Merge pull request #248 from shiguredo/feature/datachannel-signaling-type-close
DataChannel Signaling の type: close のデータ構造を定義
2 parents 6db7969 + ee591c7 commit 77fd53d

File tree

5 files changed

+70
-2
lines changed

5 files changed

+70
-2
lines changed

CHANGES.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@
3333
- @zztkm
3434
- [ADD] サイマルキャストの映像のエンコーディングパラメーター `scaleResolutionDownTo` を追加する
3535
- @zztkm
36+
- [ADD] Sora から DataChannel シグナリングを切断する際に "type": "close" メッセージを受信する機能を追加する
37+
- DataChannel シグナリングが有効、かつ ignore_disconnect_websocket が true、かつ Sora の設定で data_channel_signaling_close_message が有効な場合に受信することが可能
38+
- "type": "close" に対応するため、 `Signaling` のケースに `close` を追加した
39+
- `close` の associated value の型である `SignalingClose` 構造体を追加した
40+
- `SoraError` に DataChannel シグナリングで "type": "close" を受信して接続が解除されたことを表すケースである `dataChannelClosed` を追加した
41+
- @zztkm
3642
- [FIX] Sora から切断された場合の切断処理を修正し適切なエラーを ``MediaChannelHandlers.onDisconnect`` で受け取ることができるようにする
3743
- Sora iOS SDK 2025.1.1 までは Sora から Close Frame を受け取ったり、ネットワークエラーが起きたりしても、WebSocket メッセージ受信失敗に起因する ``SoraError.webSocketError`` しか受信できなかったが、以下の内容を受信できるようになった
3844
- Sora から Close Frame を受け取った場合のステータスコードと理由

Sora/MediaChannel.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ public final class MediaChannel {
416416
handlers.onDisconnectLegacy?(error)
417417

418418
// クロージャを用いて、エラーの内容に応じた SoraCloseEvent を生成
419-
// error が nil の場合はクライアントからの正常終了として .ok にする
419+
// error が nil の場合はクライアントからの正常終了 or DataChannel のみのシグナリング利用時の正常終了として .ok にする
420420
// error が SoraError の場合はケースに応じて .ok と .error を切り替える
421421
// error が SoraError の場合はクライアントが disconnect に渡した error のため、そのまま .error とする
422422
let disconnectEvent: SoraCloseEvent = {
@@ -428,6 +428,8 @@ public final class MediaChannel {
428428
case .webSocketClosed(let code, let reason):
429429
// 基本的に reason が nil なるケースはないはずだが、nil の場合は空文字列とする
430430
return SoraCloseEvent.ok(code: code.intValue(), reason: reason ?? "")
431+
case .dataChannelClosed(let code, let reason):
432+
return SoraCloseEvent.ok(code: code, reason: reason)
431433
default:
432434
return SoraCloseEvent.error(error)
433435
}

Sora/PeerChannel.swift

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,9 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate {
146146

147147
private var connectedAtLeastOnce: Bool = false
148148

149+
/// DataChannel シグナリングで type: close メッセージを受信したときにメッセージ内容を保存するための変数
150+
private var dataChannelSignalingClose: (code: Int, reason: String)?
151+
149152
// type: redirect のために SDP を保存しておく
150153
// 値が設定されている場合2回目の type: connect メッセージ送信とみなし、 redirect 中であると判断する
151154
private var sdp: String?
@@ -982,14 +985,17 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate {
982985

983986
func handleSignalingOverDataChannel(_ signaling: Signaling) {
984987
Logger.debug(
985-
type: .mediaStream,
988+
type: .peerChannel,
986989
message: "handle signaling over DataChannel => \(signaling.typeName())")
987990
switch signaling {
988991
case .reOffer(let reOffer):
989992
createAndSendReAnswerOverDataChannel(forReOffer: reOffer.sdp)
990993
case .push, .notify:
991994
// 処理は不要
992995
break
996+
case .close(let close):
997+
// dataChannelSignalingClose に格納した値は basicDisconnect で利用される
998+
dataChannelSignalingClose = (code: close.code, reason: close.reason)
993999
default:
9941000
Logger.error(
9951001
type: .peerChannel, message: "unexpected signaling type => \(signaling.typeName())")
@@ -1044,6 +1050,19 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate {
10441050

10451051
nativeChannel?.close()
10461052

1053+
var error = error
1054+
// DataChannel が正常にクローズされ (reason == .dataChannelClosed)、
1055+
// かつ事前に Sora から "close" メッセージを受信していた場合 (dataChannelSignalingClose != nil)、
1056+
// error を SoraError.dataChannelClosed にする
1057+
if let dataChannelSignalingClose = dataChannelSignalingClose,
1058+
case .dataChannelClosed = reason
1059+
{
1060+
error = SoraError.dataChannelClosed(
1061+
statusCode: dataChannelSignalingClose.code, reason: dataChannelSignalingClose.reason
1062+
)
1063+
}
1064+
1065+
// TODO(zztkm): signalingChannel.ignoreDisconnectWebSocket が true の場合はこの処理は不要かもしれない
10471066
signalingChannel.disconnect(error: error, reason: reason)
10481067

10491068
Logger.debug(type: .peerChannel, message: "call onDisconnect")
@@ -1055,6 +1074,9 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate {
10551074
onConnect = nil
10561075
}
10571076

1077+
// disconnect したあとは基本的に PeerChannel を使い回さないはずだが、一応 nil にしておく
1078+
dataChannelSignalingClose = nil
1079+
10581080
Logger.debug(type: .peerChannel, message: "did disconnect")
10591081
}
10601082

Sora/Signaling.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@ public enum Signaling {
132132
/// "redirect" シグナリング
133133
case redirect(SignalingRedirect)
134134

135+
/// type: "close" は DataChannel シグナリング利用時に
136+
/// `ignore_disconnect_websocket`: true かつ、`data_channel_signaling_close_message`: true の場合に受信する
137+
case close(SignalingClose)
138+
135139
/// :nodoc:
136140
public static func decode(_ data: Data) -> Result<Signaling, Error> {
137141
do {
@@ -175,6 +179,8 @@ public enum Signaling {
175179
return "switched"
176180
case .redirect:
177181
return "redirect"
182+
case .close:
183+
return "close"
178184
}
179185
}
180186
}
@@ -515,6 +521,15 @@ public struct SignalingRedirect {
515521
public var location: String
516522
}
517523

524+
/// type: "close" シグナリングメッセージを表す
525+
/// Sora から受信するメッセージなのでエンコーダーは実装していない
526+
public struct SignalingClose {
527+
/// ステータスコード
528+
public var code: Int
529+
/// 切断理由
530+
public var reason: String
531+
}
532+
518533
/// "notify" シグナリングメッセージを表します。
519534
public struct SignalingNotify {
520535
// MARK: イベント情報
@@ -692,6 +707,8 @@ extension Signaling: Codable {
692707
self = try .switched(SignalingSwitched(from: decoder))
693708
case "redirect":
694709
self = try .redirect(SignalingRedirect(from: decoder))
710+
case "close":
711+
self = try .close(SignalingClose(from: decoder))
695712
default:
696713
throw SoraError.unknownSignalingMessageType(type: type)
697714
}
@@ -1355,3 +1372,17 @@ extension SignalingRedirect: Decodable {
13551372
location = try container.decode(String.self, forKey: .location)
13561373
}
13571374
}
1375+
1376+
/// :nodoc:
1377+
extension SignalingClose: Decodable {
1378+
enum CodingKeys: String, CodingKey {
1379+
case code
1380+
case reason
1381+
}
1382+
1383+
public init(from decoder: Decoder) throws {
1384+
let container = try decoder.container(keyedBy: CodingKeys.self)
1385+
code = try container.decode(Int.self, forKey: .code)
1386+
reason = try container.decode(String.self, forKey: .reason)
1387+
}
1388+
}

Sora/SoraError.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ public enum SoraError: Error {
3838

3939
/// メッセージング機能のエラー
4040
case messagingError(reason: String)
41+
42+
/// DataChannel 経由のシグナリングで type: close を受信し、接続が解除されたことを示します。
43+
/// - statusCode: ステータスコード
44+
/// - reason: 切断理由
45+
case dataChannelClosed(statusCode: Int, reason: String)
4146
}
4247

4348
/// :nodoc:
@@ -73,6 +78,8 @@ extension SoraError: LocalizedError {
7378
return "Camera error: \(reason)"
7479
case .messagingError(let reason):
7580
return "Messaging error: \(reason)"
81+
case .dataChannelClosed(let statusCode, let reason):
82+
return "DataChannel is closed (\(statusCode) \(reason))"
7683
}
7784
}
7885
}

0 commit comments

Comments
 (0)