Skip to content

Commit 918605e

Browse files
committed
RPC 機能に対応する
1 parent 887f5e1 commit 918605e

File tree

8 files changed

+665
-0
lines changed

8 files changed

+665
-0
lines changed

CHANGES.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,19 @@
3333
- デフォルト値の `unspecified` の場合はシグナリングパラメータに `simulcast_request_rid` を含めない
3434
- role が sendrecv または recvonly の場合、かつ simulcast が true の場合にのみ有効
3535
- @zztkm
36+
- [ADD] RPC 機能を追加する
37+
- `MediaChannel``rpc` メソッドを追加する
38+
- RPC メソッドを定義するための `RPCMethodProtocol` プロトコルを追加する
39+
- RPC の ID を表す `RPCID` 列挙型を追加する
40+
- `int(Int)``string(String)` の 2 つのケースをサポート
41+
- RPC エラー応答の詳細を表す `RPCErrorDetail` 構造体を追加する
42+
- RPC 成功応答を表す `RPCResponse<Result>` ジェネリック構造体を追加する
43+
- DataChannel 経由の RPC を扱う `RPCChannel` クラスを追加する
44+
- `SoraError` に RPC 関連のエラーケースを追加する
45+
- `rpcUnavailable(reason: String)`
46+
- `rpcCallFailed(errorDetail: RPCErrorDetail)`
47+
- `rpcInvalidResponse`
48+
- @zztkm
3649

3750
### misc
3851

Sora/DataChannel.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,8 @@ class BasicDataChannelDelegate: NSObject, RTCDataChannelDelegate {
207207
type: .dataChannel,
208208
message: "decode failed (\(error.localizedDescription)) => ")
209209
}
210+
case "rpc":
211+
peerChannel.handleRPCMessage(data)
210212
case "e2ee":
211213
Logger.error(
212214
type: .dataChannel, message: "NOT IMPLEMENTED: label => \(dataChannel.label)")

Sora/MediaChannel.swift

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,21 @@ public final class MediaChannel {
154154
/// サーバーから通知を受信可能であり、接続中にのみ取得可能です。
155155
public private(set) var subscriberCount: Int?
156156

157+
/// RPC を扱うチャネル
158+
public var rpcChannel: RPCChannel? {
159+
peerChannel.rpcChannel
160+
}
161+
162+
/// RPC で利用可能なメソッド一覧
163+
public var rpcMethods: [RPCMethod] {
164+
peerChannel.rpcChannel?.allowedMethods.compactMap { RPCMethod(name: $0) } ?? []
165+
}
166+
167+
/// RPC で利用可能なサイマルキャスト rid
168+
public var rpcSimulcastRids: [String] {
169+
peerChannel.rpcChannel?.simulcastRpcRids ?? []
170+
}
171+
157172
// MARK: 接続チャネル
158173

159174
/// シグナリングチャネル
@@ -227,6 +242,58 @@ public final class MediaChannel {
227242
timeout: configuration.connectionTimeout)
228243
}
229244

245+
// MARK: - RPC
246+
247+
/// RPC をジェネリクスで送信する。
248+
public func rpc<M: RPCMethodProtocol>(
249+
method: M.Type,
250+
params: M.Params,
251+
notification: Bool = false,
252+
timeout: TimeInterval = 5.0
253+
) async throws -> RPCResponse<M.Result>? {
254+
let response = try await withCheckedThrowingContinuation {
255+
(continuation: CheckedContinuation<RPCResponse<Any>?, Error>) in
256+
guard let rpcChannel else {
257+
continuation.resume(
258+
throwing: SoraError.rpcUnavailable(reason: "rpc channel is not available"))
259+
return
260+
}
261+
_ = rpcChannel.call(
262+
methodName: method.name,
263+
params: params,
264+
notification: notification,
265+
timeout: timeout
266+
) { result in
267+
continuation.resume(with: result)
268+
}
269+
}
270+
guard let response else {
271+
return nil
272+
}
273+
return try decodeRPCResponse(response, method: method)
274+
}
275+
276+
private func decodeRPCResponse<M: RPCMethodProtocol>(
277+
_ response: RPCResponse<Any>,
278+
method: M.Type
279+
) throws -> RPCResponse<M.Result> {
280+
let decoded: M.Result
281+
do {
282+
decoded = try decodeRPCResult(response.result, as: M.Result.self)
283+
} catch {
284+
throw SoraError.rpcDecodingError(reason: error.localizedDescription)
285+
}
286+
return RPCResponse<M.Result>(id: response.id, result: decoded)
287+
}
288+
289+
private func decodeRPCResult<T: Decodable>(_ result: Any, as type: T.Type) throws -> T {
290+
let data = try JSONSerialization.data(
291+
withJSONObject: result,
292+
options: [.fragmentsAllowed])
293+
let decoder = JSONDecoder()
294+
return try decoder.decode(T.self, from: data)
295+
}
296+
230297
// MARK: - 接続
231298

232299
private var _handler: ((_ error: Error?) -> Void)?

Sora/PeerChannel.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate {
125125
var dataChannels: [String: DataChannel] = [:]
126126
var switchedToDataChannel: Bool = false
127127
var signalingOfferMessageDataChannels: [[String: Any]] = []
128+
var rpcAllowedMethods: [String] = []
129+
var rpcSimulcastRids: [String] = []
130+
var rpcChannel: RPCChannel?
128131

129132
weak var mediaChannel: MediaChannel?
130133

@@ -938,6 +941,18 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate {
938941
signalingOfferMessageDataChannels = dataChannels
939942
}
940943

944+
if let rpcMethods = offer.rpcMethods {
945+
rpcAllowedMethods = rpcMethods
946+
} else {
947+
rpcAllowedMethods = []
948+
}
949+
950+
if let simulcastRpcRids = offer.simulcastRpcRids {
951+
rpcSimulcastRids = simulcastRpcRids
952+
} else {
953+
rpcSimulcastRids = []
954+
}
955+
941956
// offer.simulcast が設定されている場合、WrapperVideoEncoderFactory.shared.simulcastEnabled を上書きする
942957
if let simulcast = offer.simulcast {
943958
WrapperVideoEncoderFactory.shared.simulcastEnabled = simulcast
@@ -1033,6 +1048,15 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate {
10331048
internalHandlers.onReceiveSignaling?(signaling)
10341049
}
10351050

1051+
/// DataChannel の rpc で受信したメッセージを処理する。
1052+
func handleRPCMessage(_ data: Data) {
1053+
guard let rpcChannel else {
1054+
Logger.warn(type: .peerChannel, message: "rpcChannel is unavailable")
1055+
return
1056+
}
1057+
rpcChannel.handleMessage(data)
1058+
}
1059+
10361060
private func finishConnecting() {
10371061
Logger.debug(type: .peerChannel, message: "did connect")
10381062
Logger.debug(
@@ -1065,6 +1089,12 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate {
10651089
message: "error: \(error.localizedDescription)")
10661090
}
10671091

1092+
if let rpcChannel {
1093+
rpcChannel.invalidate(
1094+
reason: SoraError.rpcTransportClosed(reason: reason.description))
1095+
self.rpcChannel = nil
1096+
}
1097+
10681098
sendDisconnectMessageIfNeeded(reason: reason, error: error)
10691099

10701100
if configuration.isSender {
@@ -1363,6 +1393,13 @@ class PeerChannel: NSObject, RTCPeerConnectionDelegate {
13631393
dataChannel: dataChannel, compress: compress, mediaChannel: mediaChannel,
13641394
peerChannel: self)
13651395
dataChannels[dataChannel.label] = dc
1396+
1397+
if label == "rpc" {
1398+
rpcChannel = RPCChannel(
1399+
dataChannel: dc,
1400+
rpcMethods: rpcAllowedMethods,
1401+
simulcastRpcRids: rpcSimulcastRids)
1402+
}
13661403
}
13671404
}
13681405

0 commit comments

Comments
 (0)