Files
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

39 lines
1.5 KiB
Swift

import Foundation
/// An RTP packet: 12-byte base header (plus optional CSRC/extension) and payload.
struct RtpPacket {
let header: RtpHeader
let payload: [UInt8]
init(header: RtpHeader, payload: [UInt8]) {
self.header = header
self.payload = payload
}
/// Parses a full RTP datagram. Honors CSRC lists, one-level extension
/// headers, and RFC 3550 padding, mirroring the C++ receiver.
static func parse(_ input: [UInt8]) -> RtpPacket? {
guard input.count >= 12, let header = RtpHeader.parse(input) else { return nil }
var offset = 12 + header.csrcCount * 4
guard input.count >= offset else { return nil }
if header.extensionHeader {
guard input.count >= offset + 4 else { return nil }
let extensionWords = (Int(input[offset + 2]) << 8) | Int(input[offset + 3])
offset += 4 + extensionWords * 4
guard input.count >= offset else { return nil }
}
var payloadSize = input.count - offset
if header.padding {
// RFC 3550: the last byte holds the padding size, including itself.
guard payloadSize > 0 else { return nil }
let paddingSize = Int(input[input.count - 1])
guard paddingSize != 0, paddingSize <= payloadSize else { return nil }
payloadSize -= paddingSize
}
return RtpPacket(header: header, payload: Array(input[offset..<(offset + payloadSize)]))
}
}