180 lines
7.2 KiB
Swift
180 lines
7.2 KiB
Swift
import Foundation
|
||
|
||
/// 通过 RTCDataChannel 传输的控制指令。
|
||
/// 与 control_message.proto (proto3) 二进制格式完全兼容(手写极简 protobuf 编解码,
|
||
/// 免去 SwiftProtobuf 依赖与代码生成)。
|
||
/// 坐标 x/y/x1/y1/x2/y2 均为相对屏幕的百分比,取值范围 0.0 ~ 1.0。
|
||
struct ControlMessage {
|
||
|
||
enum Action: Int {
|
||
case unknown = 0
|
||
case touch = 1
|
||
case swipe = 2
|
||
case key = 3
|
||
case longPress = 4
|
||
case motionEvent = 5
|
||
case setResolution = 6 // 控制端请求被控端切换屏幕采集分辨率
|
||
case reportResolution = 7 // 被控端上报当前实际采集分辨率
|
||
case setStreamMode = 8 // 控制端请求被控端切换屏幕串流模式
|
||
case reportStreamMode = 9 // 被控端上报当前生效的串流模式
|
||
}
|
||
|
||
var action: Action = .unknown // field 1, varint
|
||
var x: Double = 0 // field 2, fixed64
|
||
var y: Double = 0 // field 3, fixed64
|
||
var x1: Double = 0 // field 4, fixed64
|
||
var y1: Double = 0 // field 5, fixed64
|
||
var x2: Double = 0 // field 6, fixed64
|
||
var y2: Double = 0 // field 7, fixed64
|
||
var duration: Int64 = 0 // field 8, varint
|
||
var keyCode: Int32 = 0 // field 9, varint
|
||
var keyAction: Int32 = 0 // field 10, varint (0=按下 1=抬起)
|
||
var motionAction: Int32 = 0 // field 11, varint (0=DOWN 1=UP 2=MOVE 3=CANCEL)
|
||
var width: Int32 = 0 // field 12, varint
|
||
var height: Int32 = 0 // field 13, varint
|
||
var fps: Int32 = 0 // field 14, varint
|
||
var streamMode: Int32 = 0 // field 15, varint (0=WebRTC 1=自编码)
|
||
|
||
// MARK: - Encode
|
||
|
||
func serializedData() -> Data {
|
||
var w = ProtoWriter()
|
||
w.writeVarintField(1, UInt64(action.rawValue))
|
||
w.writeDoubleField(2, x)
|
||
w.writeDoubleField(3, y)
|
||
w.writeDoubleField(4, x1)
|
||
w.writeDoubleField(5, y1)
|
||
w.writeDoubleField(6, x2)
|
||
w.writeDoubleField(7, y2)
|
||
w.writeVarintField(8, UInt64(bitPattern: duration))
|
||
w.writeVarintField(9, UInt64(bitPattern: Int64(keyCode)))
|
||
w.writeVarintField(10, UInt64(bitPattern: Int64(keyAction)))
|
||
w.writeVarintField(11, UInt64(bitPattern: Int64(motionAction)))
|
||
w.writeVarintField(12, UInt64(bitPattern: Int64(width)))
|
||
w.writeVarintField(13, UInt64(bitPattern: Int64(height)))
|
||
w.writeVarintField(14, UInt64(bitPattern: Int64(fps)))
|
||
w.writeVarintField(15, UInt64(bitPattern: Int64(streamMode)))
|
||
return w.data
|
||
}
|
||
|
||
// MARK: - Decode
|
||
|
||
static func parse(from data: Data) -> ControlMessage? {
|
||
var msg = ControlMessage()
|
||
var r = ProtoReader(data: data)
|
||
while let (field, wire) = r.readTag() {
|
||
switch (field, wire) {
|
||
case (1, 0):
|
||
guard let v = r.readVarint() else { return nil }
|
||
msg.action = Action(rawValue: Int(v)) ?? .unknown
|
||
case (2, 1): guard let v = r.readDouble() else { return nil }; msg.x = v
|
||
case (3, 1): guard let v = r.readDouble() else { return nil }; msg.y = v
|
||
case (4, 1): guard let v = r.readDouble() else { return nil }; msg.x1 = v
|
||
case (5, 1): guard let v = r.readDouble() else { return nil }; msg.y1 = v
|
||
case (6, 1): guard let v = r.readDouble() else { return nil }; msg.x2 = v
|
||
case (7, 1): guard let v = r.readDouble() else { return nil }; msg.y2 = v
|
||
case (8, 0): guard let v = r.readVarint() else { return nil }; msg.duration = Int64(bitPattern: v)
|
||
case (9, 0): guard let v = r.readVarint() else { return nil }; msg.keyCode = Int32(truncatingIfNeeded: Int64(bitPattern: v))
|
||
case (10, 0): guard let v = r.readVarint() else { return nil }; msg.keyAction = Int32(truncatingIfNeeded: Int64(bitPattern: v))
|
||
case (11, 0): guard let v = r.readVarint() else { return nil }; msg.motionAction = Int32(truncatingIfNeeded: Int64(bitPattern: v))
|
||
case (12, 0): guard let v = r.readVarint() else { return nil }; msg.width = Int32(truncatingIfNeeded: Int64(bitPattern: v))
|
||
case (13, 0): guard let v = r.readVarint() else { return nil }; msg.height = Int32(truncatingIfNeeded: Int64(bitPattern: v))
|
||
case (14, 0): guard let v = r.readVarint() else { return nil }; msg.fps = Int32(truncatingIfNeeded: Int64(bitPattern: v))
|
||
case (15, 0): guard let v = r.readVarint() else { return nil }; msg.streamMode = Int32(truncatingIfNeeded: Int64(bitPattern: v))
|
||
default:
|
||
// 未知字段按 wire type 跳过
|
||
if !r.skip(wireType: wire) { return nil }
|
||
}
|
||
}
|
||
return msg
|
||
}
|
||
}
|
||
|
||
// MARK: - 极简 protobuf 读写器
|
||
|
||
private struct ProtoWriter {
|
||
var data = Data()
|
||
|
||
mutating func writeVarint(_ value: UInt64) {
|
||
var v = value
|
||
while v >= 0x80 {
|
||
data.append(UInt8((v & 0x7F) | 0x80))
|
||
v >>= 7
|
||
}
|
||
data.append(UInt8(v))
|
||
}
|
||
|
||
/// proto3:默认值(0)不编码
|
||
mutating func writeVarintField(_ field: Int, _ value: UInt64) {
|
||
guard value != 0 else { return }
|
||
writeVarint(UInt64(field << 3 | 0))
|
||
writeVarint(value)
|
||
}
|
||
|
||
mutating func writeDoubleField(_ field: Int, _ value: Double) {
|
||
guard value != 0 else { return }
|
||
writeVarint(UInt64(field << 3 | 1))
|
||
var bits = value.bitPattern.littleEndian
|
||
withUnsafeBytes(of: &bits) { data.append(contentsOf: $0) }
|
||
}
|
||
}
|
||
|
||
private struct ProtoReader {
|
||
let data: Data
|
||
var offset: Int
|
||
|
||
init(data: Data) {
|
||
self.data = data
|
||
self.offset = data.startIndex
|
||
}
|
||
|
||
mutating func readTag() -> (Int, Int)? {
|
||
guard offset < data.endIndex, let key = readVarint() else { return nil }
|
||
return (Int(key >> 3), Int(key & 0x7))
|
||
}
|
||
|
||
mutating func readVarint() -> UInt64? {
|
||
var result: UInt64 = 0
|
||
var shift: UInt64 = 0
|
||
while offset < data.endIndex {
|
||
let byte = data[offset]
|
||
offset += 1
|
||
result |= UInt64(byte & 0x7F) << shift
|
||
if byte & 0x80 == 0 { return result }
|
||
shift += 7
|
||
if shift >= 64 { return nil }
|
||
}
|
||
return nil
|
||
}
|
||
|
||
mutating func readDouble() -> Double? {
|
||
guard offset + 8 <= data.endIndex else { return nil }
|
||
var bits: UInt64 = 0
|
||
for i in (0..<8).reversed() {
|
||
bits = (bits << 8) | UInt64(data[offset + i])
|
||
}
|
||
offset += 8
|
||
return Double(bitPattern: bits)
|
||
}
|
||
|
||
mutating func skip(wireType: Int) -> Bool {
|
||
switch wireType {
|
||
case 0: return readVarint() != nil
|
||
case 1:
|
||
guard offset + 8 <= data.endIndex else { return false }
|
||
offset += 8
|
||
return true
|
||
case 2:
|
||
guard let len = readVarint(), offset + Int(len) <= data.endIndex else { return false }
|
||
offset += Int(len)
|
||
return true
|
||
case 5:
|
||
guard offset + 4 <= data.endIndex else { return false }
|
||
offset += 4
|
||
return true
|
||
default:
|
||
return false
|
||
}
|
||
}
|
||
}
|