Files
screen_cast/ios/Receiver/App/VideoSurfaceView.swift
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

35 lines
1012 B
Swift

import SwiftUI
import AVFoundation
/// Hosts the `AVSampleBufferDisplayLayer` and exposes it as a `RenderSink`.
/// The layer keeps the full screen and letterboxes via `.resizeAspect`, so the
/// decoded stream (already downscaled to the display size by the sender) is
/// shown 1:1 with no distortion.
struct VideoSurfaceView: UIViewRepresentable {
let onSink: (RenderSink) -> Void
func makeCoordinator() -> Coordinator {
Coordinator()
}
func makeUIView(context: Context) -> UIView {
let view = UIView(frame: .zero)
view.backgroundColor = .black
let layer = AVSampleBufferDisplayLayer()
layer.videoGravity = .resizeAspect
view.layer.addSublayer(layer)
let sink = AVSampleBufferRenderSink(layer: layer)
context.coordinator.sink = sink
onSink(sink)
return view
}
func updateUIView(_ uiView: UIView, context: Context) {}
final class Coordinator {
var sink: AVSampleBufferRenderSink?
}
}