Files
screen_cast/ios/Receiver/Rtp/RtpHeader.swift
T
fegger c43dd3ca4a feat(ios): native receiver app (Swift, min iOS 17)
A native iOS receiver so an iPhone can act as the second receiver, speaking the existing signaling + RTP protocol (no C++ changes) and mirroring the Android receiver (Phase 8) source-to-source.

- RTP core (header/packet, jitter buffer, H.264 depacketizer) ported from the Android receiver
- BSD-socket signaling server (dual-stack, most-recent-peer, never-throwing sends) + NSBonjourServices
- VideoToolbox H.264 decode (in-band SPS/PPS, real-time, rebuilds on size change) -> AVSampleBufferDisplayLayer
- PLI keyframe recovery (500 ms) + pendingOffer for late surface attach
- XcodeGen project + bootstrap.sh; XCTest port of the Android suite + new coverage
- .gitignore for generated artifacts; CHANGELOG; PHASES + MEMORY updated

Status: authored; on-device validation pending a Mac + Xcode 26 + iPhone 16.
2026-09-10 21:31:53 +02:00

57 lines
2.1 KiB
Swift

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]),
)
}
}