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.
This commit is contained in:
@@ -7,6 +7,50 @@ bottom of this file.
|
||||
|
||||
## Project state
|
||||
|
||||
- **Phase 9 in progress: iOS receiver app** (`ios/`, Swift, min iOS 17):
|
||||
iPhone as a second receiver. Speaks the same signaling+RTP protocol — no C++
|
||||
changes. Mirrors the Android receiver (Phase 8) source-to-source.
|
||||
- Scaffolding: XcodeGen `project.yml` (Info.plist carries
|
||||
`NSLocalNetworkUsageDescription` + `NSBonjourServices: _screencast._tcp`) +
|
||||
`bootstrap.sh` (downloads XcodeGen from the GitHub release, no Homebrew;
|
||||
generates the project; builds/tests via xcodebuild). `ios/README.md` has
|
||||
build/install/device steps.
|
||||
- App (`Receiver/App`): SwiftUI + `AVSampleBufferDisplayLayer` (`.resizeAspect`
|
||||
= letterbox — the same "size the surface, not a transform" lesson as
|
||||
Android); `ReceiverController` (ObservableObject) drives start/stop on
|
||||
scenePhase.
|
||||
- Protocol core (`Receiver/Rtp`, `Receiver/Signaling`): RtpHeader/RtpPacket/
|
||||
JitterBuffer/H264Depacketizer ported source-to-source; SignalingMessage
|
||||
(JSONSerialization) + LineAssembler + SignalingServer (BSD sockets,
|
||||
dual-stack, most-recent-peer-wins, MSG_NOSIGNAL sends that never throw);
|
||||
`AvccConverter` (Annex-B ↔ AVCC) + `NalExtractor` (SPS/PPS) are new for the
|
||||
VideoToolbox path.
|
||||
- Decode (`Receiver/Decode`): `H264FormatDescription` (Core Foundation H.264
|
||||
config recipe) + `H264VideoToolboxDecoder` (in-band SPS/PPS → session;
|
||||
`kVTDecompressionPropertyKey_RealTime`; session recreated on size change;
|
||||
the output callback may run on a worker thread, so state is lock-guarded)
|
||||
→ `AVSampleBufferRenderSink` (AVSampleBufferDisplayLayerSession).
|
||||
- Support (`Receiver/Support`): `UdpTransport` (poll-based recv so close() can't
|
||||
strand a blocked recvfrom) + `LocalAddress` (getifaddrs for the --peer hint).
|
||||
- Pipeline (`Receiver/Pipeline`): ReceiverPipeline — one serial queue for all
|
||||
state + decode; reader thread only polls/receives UDP; pendingOffer for late
|
||||
surface attach (+ PLI); PLI rate-limited 500 ms; first-frame flag; video
|
||||
size tracked (never reports a placeholder before the first real keyframe —
|
||||
the Android startup-squish lesson).
|
||||
- Tests (`ios/ReceiverTests`): the 25 Android JVM tests ported to XCTest plus
|
||||
new coverage for signaling JSON, line framing, AVCC, NAL extraction.
|
||||
- `.gitignore` excludes the generated `ios/Receiver.xcodeproj/`, `ios/tools/`,
|
||||
`ios/Receiver/Info.plist` (the project.yml `info` block is the source of
|
||||
truth).
|
||||
- **NOT YET VALIDATED** (this box is Linux, no Xcode/iOS SDK): nothing here
|
||||
compiles or runs. 9.5 needs a Mac with Xcode 26 + the iPhone 16.
|
||||
Highest-risk on-device items: (1) local-network + Bonjour consent
|
||||
(NSBonjourServices must be `_screencast._tcp`; iOS 26 tightened the prompt),
|
||||
(2) the H264FormatDescription CF ownership recipe (a bug crashes on the
|
||||
first keyframe — loud, not silent), (3) VideoToolbox decode →
|
||||
AVSampleBufferDisplayLayer render. Run `ios/bootstrap.sh test` first on the
|
||||
Mac.
|
||||
|
||||
- **Phase 8 done: Android receiver app** (`android/`, Kotlin, minSdk 30,
|
||||
app id `screen_cast.receiver`): phone as second receiver (screen → HDMI via
|
||||
USB-C DP-alt-mode). Speaks the existing signaling+RTP protocol — no C++
|
||||
|
||||
Reference in New Issue
Block a user