feat: 初始化 iOS 远程控制端项目
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user