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 is 32. 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 (range 0.0 to 1.0, default 0.2). Pixel values below this are evaluated as logical zeros.
  • thr_w: White detection threshold (range 0.0 to 1.0, default 0.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=0

To 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.txt

Burning 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.mp4

Preserving 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:

  1. 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 1
  2. Apply 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=40 to 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.