import Foundation /// JSON wire format shared with the C++ implementation: one JSON object per /// newline-terminated TCP line (offer / answer / pli). Mirrors the C++ /// `SignalingMessage` and the Kotlin receiver. enum SignalingMessage { case offer(sessionId: String, codec: String, width: Int, height: Int, frameRateNum: Int, frameRateDen: Int, rtpAddress: String, rtpPort: Int) case answer(sessionId: String, rtpAddress: String, rtpPort: Int, displayWidth: Int, displayHeight: Int) case pli(sessionId: String) static let maxMessageBytes = 64 * 1024 static func parse(_ line: String) -> SignalingMessage? { guard line.count <= maxMessageBytes else { return nil } guard let data = line.data(using: .utf8), let obj = try? JSONSerialization.jsonObject(with: data) as? [String: Any], let type = obj["type"] as? String else { return nil } switch type { case "offer": return .offer( sessionId: obj["session_id"] as? String ?? "", codec: obj["codec"] as? String ?? "", width: obj["width"] as? Int ?? 0, height: obj["height"] as? Int ?? 0, frameRateNum: obj["frame_rate_num"] as? Int ?? 30, frameRateDen: obj["frame_rate_den"] as? Int ?? 1, rtpAddress: obj["rtp_address"] as? String ?? "", rtpPort: obj["rtp_port"] as? Int ?? 0) case "answer": return .answer( sessionId: obj["session_id"] as? String ?? "", rtpAddress: obj["rtp_address"] as? String ?? "", rtpPort: obj["rtp_port"] as? Int ?? 0, displayWidth: obj["display_width"] as? Int ?? 0, displayHeight: obj["display_height"] as? Int ?? 0) case "pli": return .pli(sessionId: obj["session_id"] as? String ?? "") default: return nil } } static func serialize(_ message: SignalingMessage) -> String { let json: [String: Any] switch message { case let .offer(sessionId, codec, width, height, frameRateNum, frameRateDen, rtpAddress, rtpPort): json = [ "type": "offer", "session_id": sessionId, "codec": codec, "width": width, "height": height, "frame_rate_num": frameRateNum, "frame_rate_den": frameRateDen, "rtp_address": rtpAddress, "rtp_port": rtpPort, ] case let .answer(sessionId, rtpAddress, rtpPort, displayWidth, displayHeight): json = [ "type": "answer", "session_id": sessionId, "rtp_address": rtpAddress, "rtp_port": rtpPort, "display_width": displayWidth, "display_height": displayHeight, ] case let .pli(sessionId): json = ["type": "pli", "session_id": sessionId] } guard let data = try? JSONSerialization.data(withJSONObject: json), let s = String(data: data, encoding: .utf8) else { return "{}\n" // unreachable for our message types; serialize is total } return s + "\n" } }