Skip to content

Repository files navigation

tapecap

Raw DV / HDV tape capture over FireWire for macOS.

tapecap pulls the bitstream a DV or HDV camcorder/deck sends over FireWire (IEEE 1394) and writes it to disk exactly as it comes off the tape — nothing demuxed, nothing re-encoded:

Tape format Bus protocol What tapecap writes
DV (incl. DVCAM / Digital8) IEC 61883-2 raw DIF byte stream (.dv)
HDV IEC 61883-4 raw MPEG-2 Transport Stream, 188-byte TS packets (.m2t / .ts) — video and audio (MPEG-1 Layer II) and all PSI/metadata

Why this exists

On macOS you can already capture SD DV through AVFoundation (e.g. with ffmpeg's avfoundation input). But for HDV, AVFoundation deliberately demuxes the incoming MPEG-2 Transport Stream and only hands back the MPEG-2 video elementary stream — so you lose the audio and the transport-stream metadata. The leading DV archival tool, MediaArea/MIPoPS dvrescue, hits the same wall: on macOS it captures via AVFoundation and is DV-oriented.

The data is all there on the wire, though — Adobe Premiere Pro captures full HDV on the same machines. The trick is to bypass AVFoundation and read the raw isochronous IEC 61883 stream straight from IOKit's FireWire stack. That is exactly what Apple's own AVCVideoServices framework (from the now-retired FireWire SDK) does, via its MPEG2Receiver (HDV) and DVReceiver (DV) classes. tapecap is a small command-line front end over that framework, rebuilt as a modern universal binary.

See docs/BACKGROUND.md for the gory details.

Features

  • list / info / capture — enumerate FireWire AV/C devices, inspect a deck's capabilities and current mode/timecode, and capture the raw stream.
  • Positioning helperscue / jog / wind drive the deck without capturing, report the final position, and support --json for scripts.
  • DV / HDV auto-detect — picks the format from the deck's output-plug signal format; override with --format dv|hdv.
  • Automatic transport control — sends AV/C PLAY on start and STOP at the end; also stops on --duration, Ctrl-C, or end of tape (--no-control lets you drive the deck yourself).
  • Live status while capturing — tape SMPTE timecode, recording date/time, size and a real coded-frame count, parsed straight from the stream (DV VAUX/subcode packs and the Sony HDV MPEG-TS AUX stream), plus a running count of transport continuity errors (the tape/transport damage signal).
  • Stream diagnostics (HDV) — confirms the elementary streams present (video, audio, timecode AUX — the audio and AUX are exactly what AVFoundation drops) and reports the video geometry, frame rate and bit rate from the stream.
  • Auto-naming — with no output path, files are named from the recording's own date/time (e.g. 20101029-140926.m2t); pass - to stream to stdout.
  • Universal binary — one arm64 + x86_64 build for macOS 11–15.

Requirements

  • macOS 11 – 15. macOS 15 Sequoia is the last release that ships the IOFireWireFamily driver and the FireWire SDK headers; FireWire was removed in macOS 26 Tahoe, so tapecap cannot work there. Build and run on macOS ≤ 15.
  • Apple Silicon and Intel are both supported (universal binary).
    • Apple Silicon and recent Intel Macs have no FireWire port: use the Apple Thunderbolt-to-FireWire adapter (plus a Thunderbolt-3→2 adapter on USB-C/TB3/TB4 Macs). This is the same setup Premiere/dvrescue users rely on.
  • A DV/HDV camcorder or deck with a FireWire (i.LINK / DV) port.

Install

Homebrew

brew install xingrz/tap/tapecap

(Installs the prebuilt universal binary; no quarantine step needed.)

Download a build

Each push produces a universal binary via GitHub Actions — grab the tapecap-macos-15 artifact from the Actions tab, or download a release tarball. Then:

tar -xzf tapecap-macos-universal.tar.gz
xattr -dr com.apple.quarantine tapecap   # clear the "downloaded" quarantine
./tapecap --help

Build from source

Needs the Xcode command-line tools on macOS ≤ 15:

git clone https://github.com/xingrz/tapecap
cd tapecap
make            # -> build/tapecap (universal arm64 + x86_64)
make install    # optional, installs to /usr/local/bin

Agent skill

Rather have an AI coding assistant drive tapecap for you? Install the bundled skill into your agent (Claude Code, etc.):

npx skills add xingrz/tapecap

It teaches the agent to enumerate FireWire devices, inspect a deck, capture the untouched DV/HDV bitstream, and losslessly post-process the result.

Usage

tapecap list                       # list connected FireWire AV/C devices
tapecap info    [--guid <hex>] [--json]  # show device capabilities, mode, timecode
tapecap capture [options] [output] # capture raw stream (omit output to auto-name)
tapecap cue     [--guid <hex>] [--overlap <sec>] [--json] <timecode>  # fast-wind to a timecode
tapecap jog     [--guid <hex>] [--json] <forward|back> <sec>          # short timed fast-wind
tapecap wind    [--guid <hex>] [--timeout <sec>] [--json] <start|end> # rewind to start / wind to end

Capture options

Option Meaning
--guid <hex> Pick a device by its 64-bit GUID (default: first DV/HDV device)
--format auto|dv|hdv Force the stream format (default: auto-detect from the deck)
--duration <sec> Stop after N seconds (default: until Ctrl-C / end of tape)
--eot-timeout <ms> Auto-stop after this much silence; 0 disables (default: 5000; --seek uses at least 15000 for stream startup)
--seek <timecode> Fast-wind to this tape timecode before capturing
--until <timecode> Stop once the tape timecode passes this point
--overlap <sec> Pre-/post-roll kept around --seek / --until (default: 4)
--no-control Don't send AV/C PLAY/STOP — you press play on the deck yourself
-v, --verbose Also print the framework's internal log on stderr
[output] File to write. Omit to auto-name from the recording's date/time; use - for stdout.

Positioning output

info --json prints a single JSON object on stdout, including timecode_readable and timecode. cue, jog, and wind always print a final human-readable position on stderr:

Position: 00:12:34
Position: --:--:-- (blank/no timecode)

With --json, positioning commands also print one machine-readable line on stdout:

{"ok":true,"timecode_readable":true,"timecode":"00:12:34"}
{"ok":true,"timecode_readable":false,"timecode":null,"reason":"blank/no-timecode"}

While capturing, a live status line (tape SMPTE timecode, recording date/time, size, coded-frame count and a continuity-error count) updates in place on stderr — no flag needed. The timecode and recording date/time are read straight from the stream (DV VAUX/subcode packs and the Sony HDV MPEG-TS AUX stream). For HDV, the tool also prints once which elementary streams are present (video, audio, timecode AUX) and the video geometry / frame rate / bit rate.

The continuity-error count is detect-and-report only — it never interrupts the capture, so a damaged tape still yields the most complete raw dump possible; the count just tells you whether the transport was clean.

Examples

# Auto-detect DV vs HDV, roll the tape, stop at end of tape, and save as
# e.g. 20101029-140926.m2t (from the recording's own date/time):
tapecap capture

# Force DV, capture a fixed 60 seconds to a named file:
tapecap capture --format dv --duration 60 clip.dv

# Don't drive the transport; pipe a live HDV stream straight into ffmpeg:
tapecap capture --no-control - | ffmpeg -i - -c copy out.mkv

# Re-capture just one damaged section by tape timecode (e.g. a tapeflow gap):
tapecap capture --seek 00:12:30 --until 00:14:00 gap.m2t

# Position only — fast-wind the tape to 30:00 and stop, without capturing:
tapecap cue 00:30:00

# Short fast-wind probe, useful when parked in blank/no-timecode tape:
tapecap jog forward 3

# Rewind to the very beginning, or fast-wind to the very end (blank, no timecode):
tapecap wind start
tapecap wind end

Targeted re-capture (--seek / --until / cue)

For tools like tapeflow — which capture a worn tape several times and merge the clean frames, leaving a list of remaining gaps labelled with timecodetapecap can re-capture just one gap instead of replaying the whole tape. --seek <tc> fast-winds (AV/C fast-forward/rewind) to a tape timecode before capture; --until <tc> stops once the captured stream's timecode passes a point.

A <timecode> is HH:MM:SS (also HH:MM:SS:FF with frames ignored, MM:SS, or a bare number of seconds). These drive the transport, so they need AV/C control (not --no-control). cue / --seek need a readable current tape timecode as an anchor, not just a target timecode; they work from recorded footage, not from the blank head/tail. If the deck is parked in a no-timecode region, use jog forward <sec> / jog back <sec> and watch the final Position: (or --json) until a timecode is readable, then cue/seek. capture --seek aborts without capturing if it cannot establish that initial anchor.

Tape positioning is coarse — decks coast past a stop, and (the very reason you're re-capturing) aged tapes drop their timecode mid-travel — so the landing point is approximate. --overlap <sec> (default 4) deliberately keeps extra footage on each side so the re-capture overlaps the neighbouring good material, which is exactly what a frame-merge step wants. The seek reads the deck's AV/C timecode opportunistically and dead-reckons across timecode dropouts. tapecap cue <tc> exposes the positioning on its own, for an orchestrator that prefers to cue the deck and then run capture --no-control.

By default tapecap issues an AV/C PLAY when capture starts and STOP when it ends, so you can leave the deck alone. Use --no-control if you prefer to drive playback yourself (or the deck ignores AV/C transport commands).

Winding to the ends (wind start / wind end)

cue/--seek target a timecode, so they only reach recorded footage. The blank head and tail of a tape have no timecode, so use wind for physical ends: tapecap wind start rewinds to the very beginning, tapecap wind end fast-winds to the very end. It drives the transport and watches the deck's transport state, stopping when the deck auto-stops at the mechanical end; if a deck doesn't report that state, stop it with Ctrl-C or bound it with --timeout <sec> (default 900).

When archiving a whole tape: tapecap wind start first, then capture. Two gotchas — the blank leader at the very start looks like end-of-tape and trips the silence timeout, so pass --eot-timeout 0 for a from-the-top capture; and after a full pass the deck is parked in the blank tail (no timecode), so cue/--seek cannot lock on there. If you need another targeted capture, use short tapecap jog back 3 probes until Position: reports a readable timecode, then cue/seek from that anchor. If you need a full rewind first, run tapecap wind start, then use short tapecap jog forward 3 probes before cue/seek. Run tapecap wind start again at the end to leave the tape rewound.

Permissions

macOS may gate FireWire AV/C access behind the camera/recording privacy permission (the device shows up as a muxed A/V capture device). If a capture fails to open the device:

  • Run it once from Terminal and allow the prompt, or grant your terminal app access under System Settings → Privacy & Security → Camera.
  • Make sure no other app (iMovie, Final Cut, Premiere, dvrescue, QuickTime) is holding the device open.

What you get out, and post-processing

  • DV → a raw DIF stream. Players/ffmpeg read it directly (ffmpeg -i reel.dv …).
  • HDV → a raw MPEG-2 Transport Stream. Remux losslessly, e.g. ffmpeg -i reel.m2t -c copy reel.mkv, or demux audio/video as needed. The audio that AVFoundation drops is present here.

Status & limitations

  • Verified on real hardware. HDV capture (full video + audio + metadata) confirmed against a Sony HDR-HC9 on both a MacBook Pro 2018 (Intel, macOS 12.6) and an M1 Max MacBook Pro (Apple Silicon, macOS 15) — the latter through an Apple Thunderbolt-to-FireWire adapter. CI additionally builds and smoke-tests the universal binary on macOS 14/15.
  • This tool is built around Apple's proven AVCVideoServices code and relies on deprecated-but-present IOKit FireWire isochronous APIs. They work through macOS 15; there is no path on macOS 26+.
  • Stream format (DV vs HDV) is auto-detected from the device's output-plug signal format; if a particular deck misreports it, force it with --format hdv / --format dv. tapecap info shows what was detected.
  • Only the device's output plug 0 is used (the normal tape-playback plug).

Credits & license

  • tapecap's own code is MIT licensed — see LICENSE.
  • Bundles Apple's AVCVideoServices sample code under third_party/AVCVideoServices/ (Apple sample-code license; provenance and details in its NOTICE.md).
  • The DV/HDV recording-date and timecode parser (src/dvmeta.*) is ported from xingrz/iina-dv-timecode.
  • Thanks to the IEEE 1394 / DV archival community — dvgrab, libiec61883, and MediaArea/MIPoPS dvrescue — for keeping this knowledge alive.

About

Raw DV / HDV tape capture over FireWire for macOS.

Topics

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Contributors

Languages