import Foundation /// Minimal RTP header (RFC 3550) without extensions, mirroring the C++ /// `RtpHeader` and the Kotlin receiver. struct RtpHeader: Equatable { var version = 2 var padding = false var extensionHeader = false var csrcCount = 0 var marker = false var payloadType = 96 var sequenceNumber = 0 var timestamp = 0 var ssrc = 0 /// Serializes the bare 12-byte header; requires a version-2, extension-less header. func serialize() -> [UInt8] { var out = [UInt8](repeating: 0, count: 12) out[0] = UInt8((version & 0x0F) << 6 | (padding ? 0x20 : 0) | (extensionHeader ? 0x10 : 0) | (csrcCount & 0x0F)) out[1] = UInt8((marker ? 0x80 : 0) | (payloadType & 0x7F)) out[2] = UInt8((sequenceNumber >> 8) & 0xFF) out[3] = UInt8(sequenceNumber & 0xFF) out[4] = UInt8((timestamp >> 24) & 0xFF) out[5] = UInt8((timestamp >> 16) & 0xFF) out[6] = UInt8((timestamp >> 8) & 0xFF) out[7] = UInt8(timestamp & 0xFF) out[8] = UInt8((ssrc >> 24) & 0xFF) out[9] = UInt8((ssrc >> 16) & 0xFF) out[10] = UInt8((ssrc >> 8) & 0xFF) out[11] = UInt8(ssrc & 0xFF) return out } /// Parses a 12-byte header from the start of a datagram. static func parse(_ input: [UInt8]) -> RtpHeader? { guard input.count >= 12 else { return nil } let b0 = Int(input[0]) let b1 = Int(input[1]) let version = b0 >> 6 guard version == 2 else { return nil } return RtpHeader( version: version, padding: b0 & 0x20 != 0, extensionHeader: b0 & 0x10 != 0, csrcCount: b0 & 0x0F, marker: b1 & 0x80 != 0, payloadType: b1 & 0x7F, sequenceNumber: (Int(input[2]) << 8) | Int(input[3]), timestamp: (Int(input[4]) << 24) | (Int(input[5]) << 16) | (Int(input[6]) << 8) | Int(input[7]), ssrc: (Int(input[8]) << 24) | (Int(input[9]) << 16) | (Int(input[10]) << 8) | Int(input[11]), ) } }