Read VITC Timecode in FFmpeg Using readvitc
This guide explains how to extract and parse Vertical Interval
Timecode (VITC) from digitized analog video using FFmpeg’s
readvitc video filter. You will learn the mechanics of the
filter, how to configure its scan range and threshold parameters to
handle noisy analog transfers, and how to view or export the extracted
timecode data using FFmpeg and FFprobe.
What is the readvitc Filter?
Vertical Interval Timecode (VITC) encodes timecode information into specific horizontal scan lines within the vertical blanking interval (VBI) of analog video formats (such as Betacam, U-matic, or VHS). When tape is digitized with the VBI preserved (uncropped), these scan lines appear as alternating black-and-white patterns at the very top of the digitized frame.
The readvitc filter analyzes these lines, decodes the
Manchester-encoded binary data, and injects the extracted timecode into
frame metadata tags under the keys lavfi.readvitc.tc_str
and lavfi.readvitc.tc.
Key Parameters of readvitc
The filter accepts several arguments to fine-tune the search area and detection sensitivity:
scan_max: Specifies the maximum vertical line to scan from the top of the frame. The default is32. Setting this lower can speed up processing if your VITC is consistently located on lower lines (e.g., line 12–19).thr_b: Black detection threshold (range0.0to1.0, default0.2). Pixel values below this are evaluated as logical zeros.thr_w: White detection threshold (range0.0to1.0, default0.6). Pixel values above this are evaluated as logical ones.
For clean digital captures, the defaults usually suffice. For
degraded or noisy tape captures, adjusting thr_b and
thr_w is often necessary to achieve consistent
decoding.
Extracting and Displaying VITC with FFprobe
The quickest way to verify and parse VITC data without re-encoding
the video is through ffprobe.
To read frame-by-frame VITC metadata and print it to the terminal:
ffprobe -f lavfi -i "movie=input.mov,readvitc=scan_max=32" \
-show_entries frame_tags=lavfi.readvitc.tc_str \
-of default=noprint_wrappers=1:nokey=0To export the detected timecodes directly to a clean text file containing only the timecode strings:
ffprobe -v error -f lavfi -i "movie=input.mov,readvitc=scan_max=32" \
-show_entries frame_tags=lavfi.readvitc.tc_str \
-of csv=p=0 > timecodes.txtBurning the Decoded Timecode onto the Video
You can chain readvitc with the drawtext
filter to overlay the recovered analog timecode directly onto the video
frames for QC or proxy generation:
ffmpeg -i input.mov -vf "readvitc=scan_max=32,drawtext=fontfile=/path/to/font.ttf:text='%{metadata\\:lavfi.readvitc.tc_str}':x=50:y=50:fontsize=32:fontcolor=yellow:box=1:boxcolor=black@0.6" -c:a copy output_preview.mp4Preserving VITC as Container Timecode
If you want the parsed VITC to become the native stream-level or container-level timecode in an output file (such as ProRes in a QuickTime MOV container), copy the first detected timecode frame into the video track metadata:
Identify the first valid timecode using FFprobe:
ffprobe -v error -f lavfi -i "movie=input.mov,readvitc" \ -show_entries frame_tags=lavfi.readvitc.tc_str \ -of default=noprint_wrappers=1:nokey=1 | head -n 1Apply that start timecode when encoding the output:
ffmpeg -i input.mov -timecode 10:00:00:00 -c:v prores_ks -c:a copy output.mov
Tuning Tips for Noisy Analog Sources
- Failing Detection: If timecode drops
intermittently, increase the scan range using
scan_max=40to account for vertical jitter or improper vertical alignment. - Low Contrast VITC Lines: If the signal is washed
out, narrow the threshold gap, for example:
readvitc=thr_b=0.3:thr_w=0.5. - Interlaced Video Parity: VITC is often written to distinct lines on Field 1 and Field 2. If the field order is misconfigured, add a field-separation filter or ensure your input capture maintains native interlaced scan fields.