Skip to content

Repository files navigation

UVC DV Capture icon

uvcdv-capture

TL;DR: capture (digitize) MiniDV video from a camcorder over USB on a modern Mac — Apple Silicon or Intel, no FireWire, lossless DV — right from the tape.

brew tap boriswinner/tap && brew trust boriswinner/tap && brew install uvcdv-capture

A userspace implementation of the one video format Apple left out of macOS: the UVC DV payload. Comes as a scriptable CLI and a native macOS GUI.


Why this exists

If you have a box of MiniDV cassettes and a camcorder, you have a problem in 2026:

  • The "proper" way to capture MiniDV is FireWire — a port that modern Macs don't have, can't get through adapters on Apple Silicon, and whose OS support Apple removed entirely.
  • Analog capture (composite → USB grabber) works but re-digitizes the video: you lose quality that is sitting right there on the tape as digital data.

There is a third, almost forgotten path. Some mid-2000s camcorders — notably Panasonic's "Motion DV" models — can send the complete raw DV stream over regular USB. It's the same bit-exact data you would get over FireWire, and it uses a standard (if obscure) part of the USB Video Class specification: the stream-based DV payload format (VS_FORMAT_DV).

Windows supports that format out of the box (which is why tools like Scenalyzer "just work" there). Linux supports it (dvgrab -V). macOS does not: Apple's UVC driver implements only frame-based formats (uncompressed/MJPEG/H.264), so the camera enumerates but no capture device ever appears.

uvcdv-capture fills that gap from userspace. It talks the UVC protocol directly through libusb: finds the VideoStreaming interface, claims it (macOS allows this precisely because no Apple driver binds to a format it doesn't know), negotiates the DV format, and reads the isochronous stream. No kernel extensions, no SIP changes, no drivers.

What you get: bit-exact .dv files identical to a FireWire capture — 720×576 PAL (or 720×480 NTSC) with embedded PCM audio, timecode, and recording dates. Verified with dvrescue: 0 damaged frames, 0 bus drops on the reference hardware.

Features

  • Lossless capture to raw .dv — the archival master format every DV tool understands
  • Tape transport control over USB — play/pause/stop/rewind/fast-forward the camcorder from your Mac, read the tape timecode without capturing
  • Hands-free digitizing — start playback, capture until the tape ends, stop the tape: one command
  • Scene splitting — one file per recorded clip, detected from timecode and recording-date jumps in the DV metadata
  • On-the-fly H.264/AAC encoding — get a watchable .mp4 (6–8× smaller) instead of, or alongside, the raw master; optional 50i → 50p deinterlacing
  • Live preview — half-block graphics in the terminal, or real video in the GUI, with the live tape timecode
  • Data-loss accounting — every lost packet is counted and shown; a clean capture provably has drop=0 ovf=0
  • Native macOS GUI — everything above in one window, English/Russian

Supported hardware

Any USB device that exposes the UVC VS_FORMAT_DV descriptor. The tool is vendor-agnostic and auto-configures from the device's own descriptors.

In practice this class of devices is small: Panasonic MiniDV camcorders with the USB FUNCTION → Motion DV menu item — the NV-GS series (GS75/120/150/180/230/250/280/300/320/330/500 and siblings) and their North-American PV-GS twins. Tested end-to-end on a Panasonic NV-GS330.

Explicitly not supported (the hardware doesn't speak UVC-DV):

  • Sony "USB Streaming" — proprietary low-resolution MPEG, not DV
  • JVC / Canon MiniDV models — their USB is for stills/webcam only; DV is FireWire-only
  • Any camera in "Web Camera" USB mode — that's a 320×240 webcam, useless for archiving; switch the camera to its DV/Motion DV mode

Have a different camera with a DV-over-USB mode? See Trying an untested camera — checking takes a minute, and reports are welcome.

Installation

Homebrew (recommended for the CLI)

brew tap boriswinner/tap
brew trust boriswinner/tap    # newer Homebrew requires trusting third-party taps
brew install uvcdv-capture

This builds from source on your machine, so there are no Gatekeeper warnings, and it works on both Apple Silicon and Intel Macs (the prebuilt release binaries are arm64-only; on Intel use Homebrew or build from source — the code has nothing architecture-specific, though Intel is untested so far). For encoding, preview and GUI snapshots also install ffmpeg:

brew install ffmpeg

Prebuilt binaries

The releases page has arm64 builds of both the CLI (uvcdv-capture-*.zip) and the GUI app (UVCDV-Capture-*.app.zip, CLI included inside). They are not code-signed, so after unzipping either right-click → Open on first launch, or:

xattr -d com.apple.quarantine ./uvcdv-capture      # CLI
xattr -dr com.apple.quarantine "UVCDV Capture.app" # GUI

Building from source

Dependencies: libusb and pkg-config (Homebrew), plus the Xcode Command Line Tools (xcode-select --install — provides clang; swiftc for the GUI).

git clone https://github.com/boriswinner/uvcdv-capture.git
cd uvcdv-capture
brew install libusb pkg-config

make            # CLI  -> ./uvcdv-capture
make gui        # GUI  -> ./uvcdv-gui (run from this directory)
make app        # both -> "UVCDV Capture.app" (double-clickable bundle)

sudo make install   # optional: CLI to /usr/local/bin (PREFIX=... to change)

The CLI also builds on Linux (apt install libusb-1.0-0-dev pkg-config), where the kernel driver is auto-detached — though on Linux you can just use dvgrab -V, which is exactly why this tool doesn't need to exist there.

Camera setup (required, in this order)

  1. Power the camcorder from the AC adapter (not battery).
  2. Menu: USB FUNCTION → Motion DV (or your model's DV-over-USB mode).
  3. Switch the camera to VCR/playback mode.
  4. Connect the USB cable to the Mac — directly, no hubs if possible.

You do not need to press play on the camera: the tool can start the tape itself (--play, or the checkbox in the GUI).

Using the CLI

The one command most people need

uvcdv-capture --play --stop --idle 15 tape01.dv

Starts tape playback, captures until 15 seconds pass with no data (= end of tape), stops the tape. Wrap it in caffeinate -i to keep the Mac awake for hour-long tapes:

caffeinate -i uvcdv-capture --play --stop --idle 15 tape01.dv

Choosing outputs

The output extension decides the mode:

uvcdv-capture tape01.dv                       # raw DV only (lossless master, ~13 GB/hour)
uvcdv-capture tape01.mp4                      # H.264 only (~1.5-2 GB/hour, no raw file)
uvcdv-capture --encode tape01.mp4 tape01.dv   # both at once
uvcdv-capture --deint --crf 20 tape01.mp4     # deinterlaced 50p, smaller file

Encoding runs in a separate ffmpeg process and can never slow down or corrupt the capture — if the encoder ever fell behind, frames would be skipped in the MP4 only, counted and reported (the raw stream is untouched). Without --deint the MP4 keeps the original interlacing (correctly flagged BFF); with --deint you get smooth progressive 50 fps, nicer for direct viewing on modern screens.

For irreplaceable tapes keep the raw master: an interrupted MP4 may be unplayable (unfinalized container), while a raw .dv is valid up to the last byte written. Storage math: one 60-minute tape ≈ 13 GB raw, ≈ 1.7 GB as MP4.

Scene splitting

uvcdv-capture --split --play --stop --idle 15 tape01.dv
# -> tape01-001.dv, tape01-002.dv, ... one file per recorded clip

Cuts are detected from jumps in the tape timecode or in the embedded recording date/time (consumer camcorders keep timecode continuous across in-camera cuts, so both signals are checked — the same approach as dvgrab and Scenalyzer). Works for both raw and MP4 outputs, consistently.

Tape control without capturing

uvcdv-capture --tape status   # transport state + tape timecode + supported modes
uvcdv-capture --tape play     # also: pause, stop, ff, rew, rew-fast, eject

--tape status reads the current tape position (hh:mm:ss:ff) without touching the stream — handy for scripting batch digitization. Note: on the NV-GS330 the transport control stalls after each command until the device is reset; the tool detects this and recovers automatically (you'll see a one-line resetting device and retrying notice — it's expected).

Live preview in the terminal

uvcdv-capture --preview --play --stop --idle 15 tape01.dv

Renders the actual video with Unicode half-blocks (24-bit color where the terminal supports it), with capture stats and the live tape timecode below. Needs ffmpeg. End-to-end preview latency is ~0.3 s.

Trying an untested camera

  1. Put the camera in its DV-over-USB mode (look for "Motion DV" or "DV stream via USB" in the manual) and connect it.
  2. uvcdv-capture --list — you want your device listed with DV payload: YES.
  3. uvcdv-capture --probe — runs the full UVC negotiation without capturing. Success looks like dwMaxVideoFrameSize=144000 (PAL DV frame) (or 120000 for NTSC).
  4. uvcdv-capture --max-sec 30 test.dv, then check the file with mediainfo test.dv and dvrescue test.dv.

Please open an issue with your results (working or not, with --list and --probe --verbose output) — the tested-hardware list is currently one camera long.

Full option reference

Device selection:

option default meaning
--list list UVC video devices and whether each exposes a DV format
--vid 0xXXXX, --pid 0xXXXX any pick a camera when several are connected

Outputs and encoding:

option default meaning
positional out.dv raw DV output (lossless master)
positional out.mp4/.mov/.mkv/.m4v encoded output only, no raw file
--encode FILE off encoded copy in addition to the raw output
--deint off deinterlace: 50i → 50p (bwdif); smoother on modern screens, off keeps the original interlacing
--crf N 18 quality mode (used when --vbitrate is 0): constant quality, size varies; 18 ≈ visually lossless for DV, 20 good, 23 noticeably smaller
--vbitrate KBPS 0 (off) size mode instead of CRF: predictable file size; 3000–4000 kbps is transparent for SD DV
--preset NAME veryfast x264 speed/size trade-off (ultrafastveryslow); slower ≈ 15–20% smaller at equal quality, all faster than real time for DV
--scale WxH source size output resolution (e.g. 640x480); keep the source 720×576/720×480 for archives; auto-enables --deint
--abitrate KBPS 192 AAC audio bitrate; 192 is transparent, 128 acceptable
--split off one file per recorded scene (out-001.dv, out-002.dv, …)

Session control:

option default meaning
--play off start tape playback just before capture begins
--stop off stop the tape when capture ends
--idle SEC off stop after SEC seconds without data (end of tape)
--max-sec SEC off stop after SEC seconds (test runs)
--preview off live preview in the terminal

Tape transport (standalone, no capture):

option meaning
--tape play|pause|stop|ff|rew|rew-fast|eject send a transport command
--tape status transport state, tape timecode, supported modes

Tuning and quirk overrides (defaults are validated on real hardware — only reach for these if --probe fails or the status line shows losses):

option default meaning
--transfers N 32 isochronous transfers in flight
--pkts N 128 packets per transfer (32×128 ≈ 512 ms of queue)
--ring-mb MB 64 RAM buffer between USB and disk
--iface / --format-index / --frame-index / --alt / --ep auto force UVC parameters for cameras with quirky descriptors
--probe negotiate and print stream parameters, then exit
--drop-err off discard device-flagged packets — not recommended, they normally carry valid DV

Frontend integration (what the GUI uses; available to any script):

option meaning
--status-json one JSON status line per second + a final done event
--snapshot FILE keep FILE updated with a JPEG of the current frame (~12/s)

Reading the status line

123.4 MB | 3.46 MB/s | ok=73135 drop=0 uvc_err=7351 (0/s) hdrbad=0 ovf=0
  • drop — real bus losses. Must stay 0. If it grows: connect the camera directly (no hubs), unplug other USB devices, try --transfers 48.
  • ovf — buffer overflow (disk too slow). Must stay 0; increase --ring-mb.
  • uvc_err — packets the camera flagged. A burst in the first ~1.5 s is normal (the camera marks packets while its tape tract synchronizes; the data inside is valid and is kept). The (N/s) rate should be 0 afterwards; a persistent rate suggests dirty video heads or a damaged tape section.

Verify and post-process

brew tap amiaopensource/amiaos && brew install dvrescue mediainfo

mediainfo tape01.dv     # DV, 720x576 25fps (PAL), interlaced BFF, PCM 48kHz, timecode
dvrescue tape01.dv      # frame-level error report; no error records = clean capture

The raw .dv is understood by every editor and archival tool:

ffmpeg -i tape01.dv -c copy tape01.mov    # rewrap for editing, no re-encode
dvrescue pass1.dv pass2.dv -m merged.dv   # two-pass capture merge for precious tapes

Using the GUI

Launch UVCDV Capture.app (or ./uvcdv-gui next to the CLI binary). The GUI is a frontend over the same CLI engine — every capture it runs is identical to the equivalent terminal command, which it prints into the log pane.

Workflow:

  1. Camera — click Find camera. You should see ✅ with the device id and "(tape control available)". If it says the camera is in Web Camera mode, switch it to Motion DV and reconnect.
  2. Tape buttons — Rew / Play / Pause / Stop / FF / Status, with the tape timecode displayed large. Use them to find the right spot on the tape. They are disabled while a capture runs (the capture process owns the device) — use the Play on start / Stop at end checkboxes instead.
  3. Where to write — check raw DV, MP4, or both. Deinterlace and CRF live under the MP4 entry because they affect only the MP4 — the raw DV is always a bit-exact copy of the tape. Filenames default to a timestamp and auto-increment instead of overwriting. Optionally enable Split by scene.
  4. SessionPlay on start and Stop at end give you hands-free digitizing; the idle timeout (default 15 s) detects the end of the tape.
  5. Press Start capture. You get: live video preview, a big green "Stream clean — no data loss" banner (which turns red with details if a single packet is ever lost), per-second stats with the tape timecode, and scene events in the log. The Mac is kept awake automatically for as long as the capture runs.
  6. Press Stop capture (or just let the idle timeout fire at the end of the tape). Stopping is a clean shutdown — identical to Ctrl-C in the terminal.

The interface is English by default; switch to Russian with the language popup in the top-left (applies instantly, remembered across launches).

Troubleshooting

  • no UVC-DV device found — redo the camera setup ritual in order (AC power → Motion DV → VCR → USB). Run uvcdv-capture --list to see what the Mac actually enumerates.
  • Camera listed with DV payload: no — it's in webcam/photo USB mode, or the model doesn't do DV-over-USB at all (see Supported hardware).
  • claim_interface failed — another process holds the interface; close other capture apps (or another copy of this tool).
  • drop > 0 — starved USB bus: direct port instead of a hub, remove other USB devices, try --transfers 48.
  • ovf > 0 — slow disk (usually a nearly-full or network volume); increase --ring-mb.
  • Encoder/preview doesn't start — install ffmpeg: brew install ffmpeg.
  • First ~1.5 s of a capture look corrupted in uvc_err — that's the camera's startup sync burst; the frames are actually valid (see the status line section above).

How it works, briefly

  1. Scans USB descriptors for a VideoStreaming interface whose class-specific descriptors contain VS_FORMAT_DV, and claims it — possible on macOS exactly because Apple's driver refuses to bind to a format it doesn't implement.
  2. Negotiates UVC PROBE/COMMIT for the DV format and selects a matching isochronous alt-setting. The stream has only ~4% bus headroom, so the capture keeps ~0.5 s of transfers queued and does disk I/O on a separate thread behind a RAM ring buffer — a lost isochronous packet is unrecoverable, and the measured loss rate is zero.
  3. Writes everything, including packets the camera marks with its error bit: DV's 80-byte DIF blocks carry their own error flags and concealment, so judging data validity is the DV layer's job, not the transport's (the same philosophy as FireWire capture). This design took the measured frame-damage rate from 16.7% to 0.
  4. Tape control uses the UVC Media Transport Terminal — the same standard mechanism Windows maps to DirectShow's IAMExtTransport, plus automatic recovery from an NV-GS330 firmware quirk.
  5. Preview, encoding and GUI snapshots are separate ffmpeg subprocesses fed through independent drop-on-overflow buffers: by construction, none of them can stall the capture path.

Developer/agent documentation — architecture, protocol facts, the hardware quirk encyclopedia, and porting notes — is in AGENTS.md. The original research journal (in Russian, hypotheses and dead ends included) is preserved in git history as CONTEXT.md.

License

GPL-3.0-or-later. In short: use, modify and redistribute freely, but any distributed derivative must remain open source under the same terms.

About

Lossless MiniDV capture over USB on macOS (Apple Silicon, no FireWire) — userspace UVC DV-payload driver on libusb. Tape transport control, scene splitting, on-the-fly H.264, native GUI. Tested: Panasonic NV-GS330 (Motion DV)

Resources

Stars

10 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages