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

34 lines
1.2 KiB
Swift

import Foundation
/// Splits a byte stream into newline-terminated lines, dropping CR and
/// enforcing the 64 KB line cap the same framing the C++ and Kotlin
/// receivers use. Extracted so the logic is testable in isolation.
final class LineAssembler {
private var pending: [UInt8] = []
private let maxLine = SignalingMessage.maxMessageBytes
/// Feed a chunk of received bytes; returns the complete lines it contained.
func feed(_ chunk: [UInt8]) -> [String] {
var lines: [String] = []
for b in chunk {
if b == 0x0A { // \n
let text = String(bytes: pending, encoding: .utf8) ?? ""
pending.removeAll(keepingCapacity: true)
if !text.isEmpty { lines.append(text) }
} else if b != 0x0D { // \r
if pending.count < maxLine {
pending.append(b)
} else {
// Hostile or broken peer: drop the oversized line.
pending.removeAll(keepingCapacity: true)
}
}
}
return lines
}
func reset() {
pending.removeAll(keepingCapacity: true)
}
}