Fast, native editor for Insta360 .insv 360° video (developed against the X5), in Rust.
cargo run --release -p insv-viewer -- sample.insv # real-time viewer + editor
cargo run --release -p insv-cli -- trim sample.insv -r 5-12 -r 20.5-26 # lossless .insv cuts → trimmed/
cargo run --release -p insv-cli -- reel sample.insv -r 4-12 --yaw 30 # 1080×1920 reel → reels/
cargo run --release -p insv-cli -- inspect sample.insv # metadata / calibration / IMUViewer controls: Space play/pause the clip · ⇧Space play/pause the reel · ←/→ step a frame · 1/2/3 perspective / 360° / tiny planet · H horizon off / level / lock · S stabilization off / low / medium / high / max · R export frame 9:16 / 16:9 · I / O mark segment in / out · Delete remove segment at playhead · ⌘Z / ⇧⌘Z undo / redo · drag look around · scroll zoom.
Stitching (toolbar) joins the two lenses. The camera's stored lens calibration is ~2–3° off for video, so every clip is first calibrated from its own footage (lens alignment fitted to the overlap ring, ~0.35 s at open). Then: Fast matches the lenses' brightness/colour and shows one lens instead of a ghost where they disagree; Dynamic also measures, per frame on the GPU, how far away things are around the seam and corrects the ~3 cm parallax between the lenses (brake lines, arms); Best (default) additionally cuts along the path where the aligned lenses differ least (dynamic programming per frame, steady over time), so close hands come out whole without ghosting or warping. All three export at the same speed (decode-bound); the analysis costs ~1–2 ms per frame on the GPU.
Filter (toolbar) picks one of 10 colour looks (Vivid, Warm, Cool, Teal & Orange, Golden Hour, Film, Moody,
Dramatic, Vintage, Black & White) with a strength slider. It's graded in the view shader, so the preview matches
the export exactly at no measurable cost; lossless .insv exports are never changed.
The edit (segments, transition, song, volumes, beat grid) is saved next to the file (<file>.insv.segments).
Under the clip timeline, the reel timeline shows the segments back to back as the export joins them, with the
music lane below. Play reel plays exactly what the export will look and sound like (cross-fades included);
Play clip plays the whole source with its sound and the song following source time. Sound is the master
clock for both.
- ♪ Add music… (or drop an audio file on the window): mp3, m4a/aac, wav, aiff or flac, decoded and analysed in the background (≈0.2 s for a 3-minute song). It is normalized to −14 LUFS; the Music slider is relative to that, Original sets the camera's own sound (0% by default once a song is chosen).
- Drag the song to choose which part plays; it snaps so a bar lands on the nearest cut. Drag the yellow marker to move the bar lines; fix the tempo with BPM (×2 / ÷2 for half/double-time, Re-detect).
- Drag a boundary between segments to roll the cut (later cuts stay put); hold ⌥ to ripple (only the left segment changes, later cuts move). Boundaries snap to bars, then beats; hold ⌘ or turn off Snap to place freely. With a cross-fade, its middle is what snaps.
Export… (top right) exports the segments as lossless .insv files (Insta360 Studio-editable), one 9:16 reel
per segment, or one merged reel with cut / crossfade / fade-through-black transitions, vertical 9:16 or wide 16:9
(the frame button / R). Videos are framed exactly like the outlined frame and can use 4× supersampling and Enhance (CAS sharpening + vibrance/contrast) at ~0.4 ms/frame;
resolution up to 4K (2160×3840 / 3840×2160), H.264 or HEVC. Their sound is AAC-LC 256 kbit/s 48 kHz stereo: camera audio
cross-faded / faded with the picture (10 ms fades at hard cuts), the song underneath (1.5 s fade-out at the end;
per-segment reels get the stretch of song under their segment with 0.3 s fades), and a limiter at −1 dBFS.
CLI: insv reel sample.insv renders the editor's segments, transition and song; or override:
insv reel sample.insv -r 4-8 -r 13-15 --merge --transition crossfade --transition-s 0.5 --music song.mp3 --music-start 0:42 --music-volume 100 --original-volume 0 [--no-music] [--quality standard|high|max] [--width 1080|1440|2160] [--wide] [--stitching fast|dynamic|best] [--filter teal-orange [--filter-strength 80]] [--codec hevc] [--no-supersample] [--no-enhance]
--snapshot out.png [--frame N] [--mode perspective|equirect|planet] [--horizon off|level|lock] [--smoothing off|low|medium|high|max] [--supersample] [--enhance] [--filter NAME] [--stitching fast|dynamic|best] [--size WxH] [--fov DEG]
renders headlessly through the same GPU path and prints timings. For checking the UI without a screen,
INSV_UI_SCREENSHOT=ui.png [INSV_UI_PLAY=clip|reel] [INSV_UI_AFTER_MS=3000] insv-viewer file.insv saves a
screenshot of the window and quits.
Cuts start on the keyframe at or before the in-point (every 2 s on the X5) and end on any frame; frames are
bit-identical to the source. The MP4 is rebuilt box for box like the camera writes it, and the inst trailer is
rebuilt with IMU/exposure records trimmed to the camera's own margins and timing metadata (first/last frame clock,
epoch, duration, frame count, trailer offset, file name) updated. Output is named like a camera recording
(VID_<date>_<time>_00_<seq>.insv, clock moved to the cut start).
| hardware decode, both lenses (pipelined) | ~197 fps |
| frame → GPU (zero-copy IOSurface import) | 0.12 ms |
| render 3840×1920 360° / 1080p view | 0.34 / 0.09 ms |
| reel export 1080×1920 H.264 incl. mixed + encoded audio | ~190 fps (6.3× realtime) |
| song decode + analysis (loudness, beats, waveform), 3 min mp3 | ~0.3 s |
| lossless trim | ~100 ms per segment |
| crate | what |
|---|---|
insv |
.insv parsing: MP4 sample tables + HEVC config, trailer records, metadata protobuf, lens calibration (Mei omnidirectional model), IMU, exposures; motion (gyro+accel orientation), trim (lossless cuts), mux (MP4 writer) |
insv-decode |
VideoToolbox (macOS): pipelined hardware HEVC decoding of both lenses, H.264/HEVC encoding |
insv-render |
wgpu stitcher/reprojector (view.wgsl), zero-copy IOSurface import, offscreen render + GPU NV12 conversion |
insv-audio |
AudioToolbox (macOS): song + camera AAC decoding, loudness / beat-grid / waveform analysis, the mix (shared by playback and export), AAC encoding, real-time CoreAudio output |
insv-export |
rendered exports (vertical reels): reel layout + transitions, decode → render → encode → mux with the mixed soundtrack; the project (sidecar) format |
insv-viewer |
eframe/egui viewer and editor (segments, reel + music timeline, undo, export jobs) |
insv-cli |
inspect, trim, reel, CPU reference stitch |
mmap .insv → demux (both HEVC tracks) → VideoToolbox (HW, 3 frames in flight, 2 sessions)
→ NV12 IOSurface CVPixelBuffers → Metal textures aliasing the same memory (no copy)
→ fragment shader: view ray → IMU-levelled body frame → lens frame → Mei projection → YUV→RGB → seam blend
→ screen (viewer) | GPU RGB→NV12 → VideoToolbox H.264/HEVC → MP4 (export)
audio: camera AAC + song → mix (fades, volumes, limiter) → CoreAudio (playback clock) | AAC encode on its own thread → MP4