Skip to content

Latest commit

 

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Nami

Nami is an iOS live-streaming app written in Swift/SwiftUI. The RTMP stack, RTSP/RTP stack, MPEG-TS muxer, and the encoding pipeline are all implemented from scratch in this repo. Two protocols bring in native libraries: SRT uses the libsrt xcframework (vendored SPM binary package, checksum-pinned), and WebRTC-WHIP uses Google's libwebrtc via the stasel/WebRTC Swift package.

Features

Streaming

  • Publish live camera + microphone to RTMP / RTMPS (FLV tags, AMF0, full chunk I/O, enhanced-RTMP HEVC) and RTSP / RTSPS (ANNOUNCE → SETUP → RECORD, RTP over interleaved TCP per RFC 6184/7798/3640, periodic RTCP sender reports, Basic + Digest auth).
  • SRT — MPEG-TS over SRT (libsrt), MediaMTX streamid publish convention (srt://host:8890/path or ?streamid=publish:path), with ?passphrase= and ?latency= query support.
  • WebRTC-WHIP (whip:// / whips://) — HTTP POST/DELETE signaling. WebRTC encodes internally, so custom encoder settings and adaptive bitrate apply to the other protocols only; audio uses WebRTC's own microphone capture — the in-app gain slider does not affect WHIP audio, mute does.
  • H.264 or HEVC video (VideoToolbox), AAC-LC audio (AVAudioConverter).
  • Multiple simultaneous connections — every enabled connection streams at once.
  • Adaptive bitrate: Off / Logarithmic Descend / Ladder Ascend / Hybrid.
  • Automatic reconnect with backoff while streaming.

Main screen

  • Live preview (fill/fit), rotate camera, audio mute toggle, settings, and a 4-tab options popup:
    • Camera — torch, exposure compensation, lens (0.5×/1×/tele), zoom, white balance auto/fixed (Kelvin slider), stabilization (Off/Standard/Cine/Cine Ext/Cine++/Auto).
    • Display — preview fill/fit, stream orientation (rotates the app UI and the published stream together; locked while live), rule-of-thirds grid, real-time horizon level (CoreMotion), VU meter.
    • Audio — digital input gain, active microphone picker, preferred built-in mic position (bottom/front/back) on devices that support it.
    • Stats — per-connection state, bytes sent, measured bandwidth, queue, dropped frames, reconnects; overall fps/bitrate/duration.

Settings

  • Connections — manage RTMP(S)/RTSP(S)/SRT/WHIP(S) publish URLs with optional credentials.
  • Camera — initial camera, per-side lens, resolution (480p–4K), frame rate (24–60), stream orientation, initial zoom, stabilization, multi-cam mode (off / picture-in-picture / custom layout with an interactive drag-resize canvas).
  • Video encoding — bitrate (match-resolution or custom), keyframe interval, CBR/VBR, H.264/HEVC, HDR (HLG 10-bit best-effort), adaptive bitrate mode.
  • Audio — input kind (auto/built-in/headset/Bluetooth), mic position, mono/stereo, bitrate, sample rate, input gain.

Building & running

xcodebuild -project Nami.xcodeproj -scheme Nami \
  -destination 'platform=iOS Simulator,name=iPhone 17 Pro' build

Or open Nami.xcodeproj in Xcode 16+ and run. iOS 17+ deployment target. The project uses synchronized folder groups — new Swift files are picked up automatically without pbxproj edits.

On the simulator there is no camera, so the app switches to a synthetic animated test pattern (which still reflects zoom/torch/EV/camera-switch), and uses the Mac microphone via AVAudioEngine when available (440 Hz tone fallback).

Testing against MediaMTX

With MediaMTX running locally (RTSP :8554, RTMP :1935, SRT :8890, WHIP :8889):

  1. Settings → Connections → add rtsp://127.0.0.1:8554/mystream, rtmp://127.0.0.1:1935/mystream, srt://127.0.0.1:8890/mystream, or whip://127.0.0.1:8889/mystream (on a real device, replace 127.0.0.1 with your Mac's LAN IP).
  2. Tap GO LIVE.
  3. Verify: ffprobe rtsp://127.0.0.1:8554/mystream or play in VLC/ffplay.

Debug automation hooks (DEBUG builds)

xcrun simctl launch <UDID> me.stageguard.Nami \
  -autoURL "rtsp://127.0.0.1:8554/test,rtmp://127.0.0.1:1935/test" \
  -autoStart 1 -videoCodec hevc -uiScreen options-stats
  • -autoURL url[,url2] — inject enabled AutoTest connections
  • -autoStart 1 — go live ~2 s after the preview starts
  • -videoCodec h264|hevc — override the codec
  • -orientation portrait|landscape — override the stream/app orientation
  • -uiScreen … — deep-link a screen (options-camera|display|audio|stats, settings[-connections|-camera|-video|-audio])

Architecture

CaptureService (camera / synthetic)         AudioSessionManager
        │ CMSampleBuffers (video, PCM audio)
        ▼
BroadcastController ── PreviewFeed ─► AVSampleBufferDisplayLayer (UI)
        │ gain/mute/VU → AAC encoder;  frames → VideoToolbox encoder
        ▼ EncodedVideoFrame / EncodedAudioFrame (host-clock pts, rebased to epoch)
   StreamTransport fan-out ──► RTMPTransport (FLV/AMF0/chunks over NWConnection)
        │                 ├──► RTSPTransport (SDP/RTSP + RTP packetizers)
        │                 ├──► SRTTransport  (MPEG-TS mux over libsrt)
        │                 └──► WHIPTransport (raw frames → libwebrtc)
        └── stats loop: per-connection stats, ABR, reconnect

Key contracts live in Core/Pipeline/MediaTypes.swift, Core/Pipeline/EncoderContracts.swift, and Core/Capture/CaptureContracts.swift.

Known limitations

  • RIST is not implemented — there is no maintained iOS librist package; it would require custom cross-compilation.
  • WHIP over plain http:// works for loopback (simulator); on-device use against a LAN server needs an ATS exception in the Info.plist, or use whips://.
  • WHIP video is encoded by WebRTC itself: the bitrate is capped at the configured video bitrate, but codec, keyframe-interval, and adaptive-bitrate settings do not apply.
  • WHIP audio uses WebRTC's internal microphone capture (Opus). On a headless simulator there is no audio input device, so WHIP publishes video-only there; on real devices audio flows normally. The in-app gain slider does not affect WHIP audio (mute does).
  • HDR capture is best-effort: it prefers 10-bit x420 formats + HEVC Main10 and falls back silently when unsupported.
  • Multi-cam capture requires a multi-cam capable device; on the simulator only the layout editor is available.
  • RTMPS/RTSPS use default TLS validation (no custom certificate pinning).
  • Camera-affecting settings changed mid-stream apply after the stream stops.

License

Released under the MIT License.

Privacy and support

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages