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.
58 lines
2.5 KiB
Swift
58 lines
2.5 KiB
Swift
import XCTest
|
|
@testable import Receiver
|
|
|
|
final class JitterBufferTests: XCTestCase {
|
|
private func packet(seq: Int, ts: Int = 100) -> RtpPacket {
|
|
RtpPacket(header: RtpHeader(sequenceNumber: seq, timestamp: ts), payload: [UInt8(seq)])
|
|
}
|
|
|
|
func testInOrderReleasesImmediately() {
|
|
let jitter = JitterBuffer()
|
|
XCTAssertEqual(jitter.push(packet(seq: 1)).map { $0.header.sequenceNumber }, [1])
|
|
XCTAssertEqual(jitter.push(packet(seq: 2)).map { $0.header.sequenceNumber }, [2])
|
|
XCTAssertEqual(jitter.push(packet(seq: 3)).map { $0.header.sequenceNumber }, [3])
|
|
}
|
|
|
|
func testReordersOutOfOrderPackets() {
|
|
let jitter = JitterBuffer()
|
|
XCTAssertEqual(jitter.push(packet(seq: 1)).map { $0.header.sequenceNumber }, [1])
|
|
XCTAssertTrue(jitter.push(packet(seq: 3)).isEmpty)
|
|
XCTAssertEqual(jitter.push(packet(seq: 2)).map { $0.header.sequenceNumber }, [2, 3])
|
|
}
|
|
|
|
func testOverflowReleasesInOrderAndAdvances() {
|
|
let jitter = JitterBuffer(maxDepth: 4)
|
|
XCTAssertEqual(jitter.push(packet(seq: 1)).map { $0.header.sequenceNumber }, [1])
|
|
// seq 2 is lost; 3..6 stay buffered (within the depth bound).
|
|
for seq in 3...6 {
|
|
XCTAssertTrue(jitter.push(packet(seq: seq)).isEmpty)
|
|
}
|
|
// seq 7 overflows the buffer: 3..7 flush in order.
|
|
XCTAssertEqual(jitter.push(packet(seq: 7)).map { $0.header.sequenceNumber }, [3, 4, 5, 6, 7])
|
|
// Delivery continues in order afterwards.
|
|
XCTAssertEqual(jitter.push(packet(seq: 8)).map { $0.header.sequenceNumber }, [8])
|
|
XCTAssertEqual(jitter.push(packet(seq: 9)).map { $0.header.sequenceNumber }, [9])
|
|
}
|
|
|
|
func testDiscardsStragglers() {
|
|
let jitter = JitterBuffer(maxDepth: 4)
|
|
_ = jitter.push(packet(seq: 1))
|
|
for seq in 3...8 {
|
|
_ = jitter.push(packet(seq: seq))
|
|
}
|
|
XCTAssertTrue(jitter.push(packet(seq: 9)).isNotEmpty)
|
|
// seq 4 is now far behind the expected sequence: discarded, not delivered.
|
|
XCTAssertTrue(jitter.push(packet(seq: 4)).isEmpty)
|
|
// In-order delivery continues from 10.
|
|
XCTAssertEqual(jitter.push(packet(seq: 10)).map { $0.header.sequenceNumber }, [10])
|
|
}
|
|
|
|
func testClearResetsState() {
|
|
let jitter = JitterBuffer()
|
|
_ = jitter.push(packet(seq: 5))
|
|
jitter.clear()
|
|
// A completely different sequence now starts fresh.
|
|
XCTAssertEqual(jitter.push(packet(seq: 100)).map { $0.header.sequenceNumber }, [100])
|
|
}
|
|
}
|