c43dd3ca4a
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.
57 lines
2.1 KiB
Swift
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]),
|
|
)
|
|
}
|
|
}
|