Drop-frame vs Non-drop Timecode: Diagnosing and Fixing Caption Drift
← 博客
🎬 字幕与隐藏字幕8 min read

Drop-frame vs Non-drop Timecode: Diagnosing and Fixing Caption Drift

💡 Drop-frame timecode (semicolons: HH:MM:SS;FF) is required for 29.97fps NTSC captions. Without it a 30-minute program drifts roughly two seconds out of sync. Run ffprobe -v error -select_streams v:0 -show_entries stream=r_frame_rate,field_order -of default input.mp4 to confirm your source frame rate first. The output r_frame_rate=30000/1001 means 29.97fps, so drop-frame is required.

Key takeaways

  • Drop-frame = semicolons: HH:MM:SS;FF. Non-drop = colons: HH:MM:SS:FF.
  • 29.97fps source (r_frame_rate=30000/1001 in ffprobe): always use drop-frame for NTSC broadcast captions.
  • SeConv export flags: ScenaristClosedCaptionsDropFrame for 29.97 DF, ScenaristClosedCaptions for 30fps NDF.
  • 1080i59.94 trap: ffprobe may show r_frame_rate=60000/1001 (field rate). Caption timing still runs at 29.97fps (the frame rate), not 59.94.
  • Fix timing drift in Subtitle Edit: Subtitle menu > Change frame rate, enter source rate and target rate.

What is drop-frame timecode, and why does 29.97fps need it?

NTSC color video runs at exactly 30000/1001 frames per second, approximately 29.97fps. That is about 0.1% slower than a clean 30fps. The discrepancy was introduced in 1953 when color was added to the US television standard: the color subcarrier frequency had to be shifted slightly to avoid interference with the audio carrier, and the frame rate came down with it.

That 0.1% difference accumulates quickly in broadcast timecode. Over 10 minutes it amounts to roughly 1.8 seconds of drift; over an hour it reaches about 3.6 seconds. A timecode reading "01:00:00:00" at the end of an hour of 29.97fps content would be 3.6 seconds behind actual wall-clock time if nothing compensates for the difference.

Drop-frame timecode, defined in SMPTE ST 12-1, solves this by skipping frame numbers 0 and 1 at the start of every minute, except every 10th minute (at :00, :10, :20, :30, :40, :50). No actual video frames are removed; only two timecode labels per minute are skipped. This removes 18 frame numbers from every 10-minute block, keeping the timecode clock aligned with wall time to under two frames per hour.

The notation difference is a single character: drop-frame uses a semicolon before the frame count (HH:MM:SS;FF), non-drop uses a colon (HH:MM:SS:FF). In SCC files I can confirm this at a glance: every timecode line in a NTSC broadcast SCC must show semicolons. Colons mean non-drop was used during export and the captions will drift on a real broadcast decoder.

How do I read my video's frame rate with ffprobe?

Before timing any caption or subtitle file, I always run ffprobe on the source video. The r_frame_rate field gives the container frame rate as an exact fraction, and field_order tells me whether the video is interlaced. Both matter for caption timing decisions.

# Read frame rate and interlace mode from the first video stream
ffprobe -v error -select_streams v:0 \
  -show_entries stream=r_frame_rate,field_order \
  -of default \
  input.mp4

# Read embedded timecode from stream tags (MOV, MXF, MPEG sources)
ffprobe -v error -select_streams v:0 \
  -show_entries stream_tags=timecode \
  -of default \
  input.mov

# Read timecode from format metadata (DV, GXF, some AVI and MXF sources)
ffprobe -v error \
  -show_entries format_tags=timecode \
  -of default \
  input.mxf

What the r_frame_rate output means for caption timing:

  • 30000/1001 - 29.97fps NTSC. Use drop-frame. SeConv: ScenaristClosedCaptionsDropFrame --fps:29.97
  • 30/1 - 30fps exactly. Use non-drop. SeConv: ScenaristClosedCaptions --fps:30
  • 25/1 - 25fps PAL. No drop-frame needed. Timecode uses colons throughout.
  • 24000/1001 - 23.976fps cinema or streaming. No drop-frame in most workflows.
  • 60000/1001 on an interlaced file - check field_order next (see the 1080i section below).

For the TAG:timecode output: a value like 01:00:00;00 (semicolons) confirms the source master uses drop-frame. A value like 01:00:00:00 (all colons) is non-drop. The caption file's timecode type must match the master's.

Drop-frame vs non-drop: specification comparison

I keep this table in my project notes as a quick check during format setup. It covers the differences that matter for caption delivery.

FeatureDrop-frame (DF)Non-drop (NDF)
Timecode separatorSemicolons: HH:MM:SS;FFColons: HH:MM:SS:FF
Frame rate29.97fps (30000/1001)30fps (30/1), or PAL/24fps masters
Drift vs wall clock (1 hr)Under 2 frames~3.6 seconds at 29.97fps
Frame numbers skipped0 and 1 every minute, except every 10thNone
SeConv SCC export flagScenaristClosedCaptionsDropFrameScenaristClosedCaptions
Typical use caseUS broadcast (NTSC), US streaming platformsPAL content, 30fps archival, non-NTSC masters
StandardSMPTE ST 12-1SMPTE ST 12-1

Why does my 1080i video show 59.94, and which rate do I use for captions?

This is the trap I see most often in broadcast projects. A 1080i NTSC master uses interlaced scanning: each frame is split into two fields captured at slightly different moments, giving 59.94 fields per second. Some ffprobe containers report that field rate rather than the frame rate. You run ffprobe and see r_frame_rate=60000/1001, which looks like 59.94fps.

The video content is still 29.97 frames per second. The field_order=tt or field_order=bb value in ffprobe output is the tell: it confirms the video is interlaced. For a 1080i59.94 master, the correct caption timing rate is 29.97fps, not 59.94fps. Captioning at 59.94fps produces timecodes that run at twice the rate of the program clock: a caption timed to appear at the 60-second mark would hit at 30 seconds instead.

My working rule: when field_order is anything other than progressive, I treat the caption frame rate as half the reported r_frame_rate. If ffprobe shows r_frame_rate=60000/1001 and field_order=tt, the caption frame rate is 30000/1001 = 29.97fps, drop-frame.

How do I fix drifting captions in Subtitle Edit?

When a caption file was created at the wrong frame rate, I use Subtitle Edit's Change frame rate dialog. This multiplies every timestamp in the file by the ratio between the target and source rates, correcting the drift across the entire file without touching any caption text.

Path in Subtitle Edit: Subtitle menu > Change frame rate. In the dialog, set "From" to the rate the captions are currently timed to (the wrong rate), and "To" to the correct rate. For example, captions accidentally timed to 25fps that should be at 29.97fps:

# Subtitle Edit: Subtitle > Change frame rate
# From: 25
# To:   29.97
# Every timestamp is multiplied by 29.97 / 25 = 1.19880
# A cue at 1:00.000 becomes 1:11.928 after the correction

After correcting the timestamps, I export the SCC using the correct drop-frame format flag:

# Export corrected captions as a 29.97 drop-frame SCC (NTSC broadcast)
seconv.exe corrected.srt ScenaristClosedCaptionsDropFrame --fps:29.97 --output-folder:out --overwrite

I then open the output SCC in a text editor to confirm the first few timecodes use semicolons (00:00:01;15 not 00:00:01:15), and run a caption-inspector decode check before submitting. Two minutes here prevents a broadcaster rejection later.

What can I do myself, and when is a broadcast specialist worth it?

Diagnosing a frame-rate mismatch is free and fast: ffprobe is included with ffmpeg, Subtitle Edit is open-source, and the Change frame rate correction takes one dialog. For a straightforward case where the source video and the caption file both have known frame rates, these tools handle the fix completely.

Where a specialist saves time and avoids risk: when the source master itself has incorrect or inconsistent timecode baked in, when the deliverable requires a 708-service format like SMPTE-TT rather than plain SCC, or when the project goes through a broadcaster's automated ingest with specific QC requirements. US broadcast and streaming platform deliverables that require FCC compliance are the cases where I run the complete caption QC workflow end-to-end, from initial frame-rate diagnosis through format-correct SCC export and decode verification.

FAQ

What happens if I use non-drop timecodes in a 29.97fps SCC?

The captions drift progressively further out of sync as the program runs. Non-drop and drop-frame timecodes start at the same point but diverge at 0.1%, roughly 1.8 seconds per 10 minutes. At the 30-minute mark, captions timed in non-drop will appear about 1.8 seconds late (or early, depending on direction). Broadcast QC systems typically detect and flag this automatically during ingest.

Can I tell just by looking at an SCC file whether it uses drop-frame?

Yes. Open the SCC in any plain text editor. The file starts with the header line Scenarist_SCC V1.0, then each caption line begins with a timecode. Semicolons before the frame count mean drop-frame: 00:01:00;02. Colons mean non-drop: 00:01:00:02. A 29.97fps NTSC broadcast SCC must show semicolons. If you see all colons in a file exported for NTSC, re-export using ScenaristClosedCaptionsDropFrame in SeConv.

Why does ffprobe show 60000/1001 for my 1080i master?

Some demuxers report the field rate (59.94 fields per second) rather than the frame rate (29.97 frames per second) for interlaced video. The field_order value in the ffprobe output is the indicator: tt (top-field-first) or bb (bottom-field-first) means the content is interlaced. For any interlaced source, the caption timing frame rate is half the reported r_frame_rate value. 1080i59.94 runs captions at 29.97fps, drop-frame.

How do I check a completed SCC for timecode errors before submitting?

I run a two-step check. First, open the SCC in a text editor and confirm the first several timecodes use semicolons for a 29.97 DF file. Second, decode it with caption-inspector:

docker run --rm -v /path/to/captions:/data nebulabroadcast/caption-inspector -o /data /data/file.scc

The -C1.608 output shows the decoded text and control codes as a real CEA-608 decoder would see them. Timing anomalies, skipped captions, and character errors all show up there. Details on interpreting this output are in the caption QC workflow guide.

What is the drift for a 60-minute program with the wrong timecode type?

At 29.97fps, non-drop timecode runs 3.6 seconds fast per hour relative to wall clock time. A 60-minute program captioned in non-drop when it should be drop-frame will have captions arriving about 3.6 seconds off at the end of the hour. The drift is proportional: 10 minutes = ~1.8 seconds, 30 minutes = ~5.4 seconds. Drop-frame timecode, as defined in SMPTE ST 12-1, reduces this to under two frames per hour.

Official Sources

  • ffprobe documentation (ffmpeg.org) - -show_entries, -select_streams, stream fields r_frame_rate and field_order, stream tags and format tags for timecode. Verified October 2026.
  • SMPTE timecode (Wikipedia) - Drop-frame vs non-drop mechanics, semicolon/colon notation, the 3.6 seconds/hour drift figure, frame-number skipping pattern. Verified October 2026.
  • Subtitle Edit / SeConv (nikse.dk) - Change frame rate dialog, SeConv format names ScenaristClosedCaptionsDropFrame and ScenaristClosedCaptions, export flags. Verified October 2026.
  • Comcast caption-inspector (GitHub) - Docker decode command, output file descriptions for SCC QC verification. Verified October 2026.

Written by Dao Huy (Lucas), Vietnamese translator & localization specialist (EN · ZH · FR → Vietnamese). See translation services →

报价WhatsApp