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

78 lines
2.9 KiB
Markdown

# screencast iOS receiver
A native iOS receiver app (Swift) so an iPhone can act as a second receiver.
It speaks the **existing signaling + RTP protocol unchanged** — no C++ changes —
mirroring the Android receiver (Phase 8) source-to-source.
- **Min iOS:** 17 (validated on iPhone 16 / iOS 26.6.1)
- **No third-party dependencies** — only Apple system frameworks (SwiftUI,
AVFoundation, VideoToolbox, CoreMedia, BSD sockets).
## Pipeline
```
Bonjour advertise (_screencast._tcp) + TCP signaling server (offer → answer)
UDP RTP → jitter buffer (16 pkt / 60 ms) → depacketize (single-NAL + FU-A)
→ VideoToolbox H.264 (in-band SPS/PPS) → AVSampleBufferDisplayLayer (letterbox)
```
Recovery matches the C++ receiver: a damaged frame is dropped and a PLI
(rate-limited to one per 500 ms) asks the sender for a keyframe.
## Layout
```
Receiver/
App/ SwiftUI app, video surface (AVSampleBufferDisplayLayer), controller
Rtp/ RtpHeader, RtpPacket, JitterBuffer, H264Depacketizer, Avcc, NAL extraction
Signaling/ SignalingMessage (JSON), LineAssembler, SignalingServer (BSD sockets)
Decode/ RenderSink, H.264 format description, VideoToolbox decoder
Support/ UdpTransport (poll-based), LocalAddress
Pipeline/ ReceiverPipeline (coordinates everything)
ReceiverTests/
XCTest port of the Android JVM tests + added coverage (signaling, line
framing, AVCC, NAL extraction)
```
## Build and test (on a Mac with Xcode 26)
```sh
./bootstrap.sh # installs XcodeGen, generates the project, builds for device
./bootstrap.sh test # runs the unit tests on the simulator
```
`bootstrap.sh` downloads XcodeGen from the GitHub release into `tools/` (no
Homebrew). Override the simulator with `IOS_DEST="platform=iOS Simulator,name=…"`.
## Install on a device
Free provisioning (or your team) is set in Xcode — this can't be scripted:
1. Open `Receiver.xcodeproj`.
2. Select the **Receiver** target → *Signing & Capabilities* → set your Apple ID
(a 7-day development certificate is fine for sideloading).
3. Run to the iPhone (USB, trust the computer).
The first run prompts for **Local Network** access — allow it, or the sender
will never discover the phone (silent failure, like the Android
`PROTOCOL_DNS_SD` bug).
## Stream to the phone
On the sender (Linux desktop):
```sh
screencast --send --peer <phone-ip>:5005 # target the phone directly
screencast --discover # or list receivers; the phone shows up
```
Expected: the phone shows the captured desktop, letterboxed to its screen, with
PLI recovery on Wi-Fi loss.
## Testing notes
The pure protocol core (RTP, jitter, depacketizer, signaling JSON, line framing,
AVCC, NAL extraction) is unit-tested in the simulator. The VideoToolbox decode
path and the local-network/Bonjour flow are on-device validation items — the
highest-risk parts to verify on the borrowed Mac + iPhone.