Files
VibeCoding/webrtc_controller_ios/web_rtc_controller_ios/Control/ControlMessage.swift
TongTongStudio 362b9f238a feat: 支持手动切换采集帧率
在 Android、Web 及 Flutter 控制端新增帧率选择 UI,被控端按屏幕刷新率生成支持的帧率档位并上报,允许在保持分辨率不变的情况下仅切换帧率。

另附带更新信号服务器的数据库配置。
2026-07-31 03:26:40 +08:00

205 lines
8.5 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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=)
/// REPORT_RESOLUTION
var supportedFps: [Int32] = [] // field 16, repeated varint (packed)
// 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)))
// repeated int32 supported_fps = 16packed
if !supportedFps.isEmpty {
var payload = ProtoWriter()
for v in supportedFps {
payload.writeVarint(UInt64(bitPattern: Int64(v)))
}
w.writeVarint(UInt64(16 << 3 | 2))
w.writeVarint(UInt64(payload.data.count))
w.data.append(payload.data)
}
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))
case (16, 2):
// packed repeated int32 varint
guard let len = r.readVarint() else { return nil }
let end = r.offset + Int(len)
guard end <= data.endIndex else { return nil }
while r.offset < end {
guard let v = r.readVarint() else { return nil }
msg.supportedFps.append(Int32(truncatingIfNeeded: Int64(bitPattern: v)))
}
case (16, 0):
// packed varint
guard let v = r.readVarint() else { return nil }
msg.supportedFps.append(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
}
}
}