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.
35 lines
1012 B
Swift
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?
|
|
}
|
|
}
|