How to Read VITC in FFmpeg with readvitc
This guide explains how to use FFmpeg's readvitc filter
to parse Vertical Interval Timecode (VITC) from interlaced analog video
captures. You will learn the correct filter syntax, how to adjust
detection thresholds for noisy analog signals, how to print the
extracted timecode to a text file or terminal, and how to burn the
timecode directly onto the video frames.
Understanding the readvitc Filter
Analog tape formats like Betacam, U-matic, and broadcast VHS often
encode timecode data into the non-visible scan lines of the Vertical
Blanking Interval (VBI). When these tapes are captured without cropping
the top blanking lines, the readvitc filter can read this
binary data and expose it as frame metadata under the key
lavfi.readvitc.tc_str.
For the filter to work, the video capture must include the top vertical blanking lines (typically lines 10–20 in NTSC or lines 6–22 in PAL), and the video must not be cropped or deinterlaced prior to running the filter.
Filter Options
The readvitc filter supports the following optional
parameters:
scan_max: Sets the maximum field line to scan for VITC data. The default is32. If your capture includes standard VBI space, the default is usually sufficient, but you can lower or raise it depending on where the timecode sits.thr_b: Sets the black/white threshold for black (0.0 to 1.0). The default is0.2.thr_w: Sets the black/white threshold for white (0.0 to 1.0). The default is0.6.
Adjusting thr_b and thr_w is useful when
dealing with weak or degraded analog signals where standard luma levels
have shifted.
Extracting and Printing VITC Data
To scan a video file and print every detected timecode value into a
text file, combine readvitc with the metadata
filter:
ffmpeg -i input_capture.mov -vf "readvitc=scan_max=32,metadata=mode=print:key=lavfi.readvitc.tc_str:file=vitc_log.txt" -f null -This command parses the entire file without re-encoding video frames,
creating vitc_log.txt containing entries such as:
frame:0 pts:0 pts_time:0
lavfi.readvitc.tc_str=10:14:32:04
frame:1 pts:1001 pts_time:0.0333667
lavfi.readvitc.tc_str=10:14:32:05
Reading VITC with ffprobe
If you need to query a specific frame's VITC via script without
processing the entire file with FFmpeg, use ffprobe:
ffprobe -v error -f lavfi -i "movie=input_capture.mov,readvitc=scan_max=32" -show_entries frame_tags=lavfi.readvitc.tc_str -of default=noprint_wrappers=1:nokey=0Burning VITC onto the Video
To create a reference or review copy that visibly displays the
original tape timecode, pipe the metadata string into the
drawtext filter:
ffmpeg -i input_capture.mov -vf "readvitc=scan_max=32,drawtext=fontfile=/path/to/font.ttf:text='%{metadata\:lavfi.readvitc.tc_str}':x=w-tw-40:y=40:fontsize=32:fontcolor=white:box=1:boxcolor=black@0.6" -c:v libx264 -crf 18 -c:a copy preview_vitc.mp4In this command:
readvitcextracts the timecode on a per-frame basis.drawtextaccesses the metadata using%{metadata\:lavfi.readvitc.tc_str}and renders it in the top-right corner over a semi-transparent black box.
Troubleshooting Weak Signals
If readvitc fails to detect timecode on an analog
capture:
Verify VBI Presence: Open the raw capture in a player and confirm that flickering white dotted lines are visible along the very top of the frame.
Loosen Thresholds: Broaden the detection margins to accommodate luma drift by adjusting
thr_bdown andthr_wup:readvitc=scan_max=36:thr_b=0.15:thr_w=0.50Check Processing Order: Ensure
readvitcis the absolute first filter in your filterchain. Cropping (crop), scaling (scale), or deinterlacing (yadif,bwdif) beforereadvitcwill corrupt the raw waveform lines and prevent decoding.