Skip to content

NazarKozak/SurfaceRecorderSDK

Repository files navigation

SurfaceRecorderSDK

CI Swift 6 Platforms SPM License

Record anything on iOS to video — an AR scene, the camera, or any view — with synced audio, without ReplayKit's permission prompt.

SurfaceRecorderSDK is a small, Swift-concurrency-native recording SDK. You give it a source (RealityKit AR, the device camera, or a UIKit/SwiftUI view) and it writes an MP4 — frames recycled from a pool, audio time-synced to the video clock, the whole pipeline driven by a single actor.

(GIF placeholder: recording an AR scene with a live FPS / CPU / MEM HUD)

import SurfaceRecorderSDK

let recorder = SurfaceRecorder(
    source: ARViewFrameSource(arView),   // or CameraFrameSource(), UIViewFrameSource(window), …
    audio: .microphone                   // or .none (default — no permission prompt)
)

try await recorder.start()
// … user does stuff …
let url = try await recorder.stop()      // MP4 in the temp dir, ready to share or save

Features

  • 🎥 Four sources, one recorder — AR (camera + 3D), AR camera-only, the device camera, or any view.
  • 🚫 No ReplayKit prompt — in-app capture of your own content; nothing to authorize.
  • GPU-fast ARARViewFrameSource taps postProcess and never touches the CPU rasterizer.
  • 🎙 Audio that lines up — microphone capture with CMSampleBuffer time-correction and AVAudioSession/ARSession coexistence. Off by default — no mic prompt unless you ask.
  • 📐 Resolution cap — downscale large screens to keep capture cheap.
  • 📊 Live perf readoutSurfacePerformanceMonitor (FPS / CPU / memory) you can show on screen.
  • 🧱 Swift 6, async-native — actors, AsyncSequence, strict concurrency.

Install

Swift Package Manager:

.package(url: "https://github.com/NazarKozak/SurfaceRecorderSDK.git", from: "0.1.0")

…and add "SurfaceRecorderSDK" to your target's dependencies. Requires iOS 17+.

Tracking the latest instead of a release? Use branch: "main".

Sources

Everything records through a FrameSource. Pick the one that matches what you want on tape:

Source Captures How Cost
ARViewFrameSource RealityKit AR: camera + 3D content ARView.renderCallbacks.postProcess → Metal blit 🟢 GPU
RealityKitFrameSource AR camera feed only (no 3D/UI) zero-copy ARFrame buffer, GPU rotate/convert 🟢 GPU
CameraFrameSource a plain device camera (back/front), with preview AVCaptureSession video output 🟢 GPU
UIViewFrameSource any UIView / the whole screen (UIKit + SwiftUI) single-pass drawHierarchy, pooled buffers 🟡 CPU
// AR scene — camera + 3D, recommended for ARKit/RealityKit.
SurfaceRecorder(source: ARViewFrameSource(arView), audio: .microphone)

// AR camera feed only (clean), with rotation control.
SurfaceRecorder(source: RealityKitFrameSource(arView, orientation: .portrait))

// The device camera (like a camera app). Expose `.session` to show a preview.
let camera = CameraFrameSource(position: .back, fps: 60)
camera.startRunning()                       // drives the preview
SurfaceRecorder(source: camera)

// Any view or the key window — screen capture, NO ReplayKit prompt.
SurfaceRecorder(source: UIViewFrameSource(window, maxDimension: 1080))

No-permission screen capture

UIViewFrameSource renders your app's own view hierarchy with drawHierarchy, so it never triggers the system screen-recording prompt. Trade-off vs ReplayKit: it captures only your app (not other apps or system UI) — exactly what an in-app recorder wants.

drawHierarchy runs on the main thread and its cost scales with output pixels, so use maxDimension: to cap resolution on large screens. To also capture Metal-backed content (an ARView) in the same pass, pass afterScreenUpdates: true (heavier).

Audio

Audio is opt-in — the recorder defaults to .none, so nothing prompts the user:

SurfaceRecorder(source: , audio: .microphone)   // mic, time-synced to the video clock
SurfaceRecorder(source: , audio: .none)          // silent (default)

The microphone path manages AVAudioSession (interruptions, route changes) and re-times each sample buffer to the video session clock, so audio and video stay in lockstep even alongside ARKit.

Output & sharing

stop() returns the MP4 URL in the temp directory. Move it where you like, or hand it straight to a ShareLink:

let url = try await recorder.stop()
ShareLink(item: url)                // AirDrop / Save Video / Files

Performance

SurfaceRecorderSDK is built to be cheap:

  • GPU AR pathARViewFrameSource blits the composited render texture into a pixel buffer; no CPU rasterization.
  • Zero-copy AR cameraRealityKitFrameSource hands the ARFrame buffer straight to a GPU convert/rotate.
  • Single-pass UI captureUIViewFrameSource renders into the pixel buffer's backing CGContext, no intermediate UIImage.
  • Recycled buffers — every source pulls from a CVPixelBufferPool.
  • Serialized, lock-free — one actor owns the writer; no manual locking.

Watch it live with SurfacePerformanceMonitor:

@State private var perf = SurfacePerformanceMonitor()
// …
.onAppear { perf.start() }
Text(perf.summary)   // "60 fps · 8% cpu · 124 MB"

Demo

Open Demo/SurfaceRecorderSDKDemo.xcodeproj in Xcode, pick a simulator or your device, and run. The demo has:

  • three tabs — Screen, ARKit, Camera;
  • an FPS selector (30 / 60 / Max) and a microphone toggle;
  • a gallery that saves recordings to Documents and plays them back;
  • a live FPS / CPU / MEM overlay.

AR and camera capture need a real device (the simulator has no camera).

The project depends on the local package and uses Xcode's file-system-synchronized groups — drop a .swift file into Demo/Sources/ and it's picked up automatically. (swift build only compiles the library, not the iOS demo.)

Why SurfaceRecorderSDK

ReplayKit ARVideoKit SurfaceRecorderSDK
No permission prompt (in-app capture)
RealityKit ARView (camera + 3D) ⚠️
Plain device camera + preview
Any UIKit/SwiftUI view / screen
Audio time-synced to the video clock ⚠️
Swift 6 / async-native
Maintained for the RealityKit era ⚠️

Requirements

  • iOS 17+
  • Swift 6 / Xcode 16+
  • For AR/camera capture: a real device. Add NSCameraUsageDescription (and NSMicrophoneUsageDescription if you record audio) to your Info.plist.

Roadmap

  • GPU AR recording — camera + 3D (ARViewFrameSource)
  • AR camera-only, zero-copy with orientation control (RealityKitFrameSource)
  • Device camera with preview (CameraFrameSource)
  • No-permission screen/UI capture, single-pass + resolution cap (UIViewFrameSource)
  • Mic audio with time-sync + AVAudioSession/ARKit coexistence
  • Live performance monitor
  • GPU overlay compositing — UI/HUD over AR at full frame rate (ARViewFrameSource.setOverlay)
  • GPU overlay for CameraFrameSource too
  • ARSCNViewFrameSource (SceneKit) + "migrate from ARVideoKit" guide
  • Photo / GIF / Live Photo capture
  • visionOS, HEVC, pause/resume

License

MIT — see LICENSE.

About

Record anything on iOS to video — an AR scene, the camera, or any view — with synced audio, without ReplayKit's permission prompt. Swift 6, GPU-fast.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages