Map Closed Captions in FFmpeg Using Copyts
This guide explains how to properly map and preserve closed caption
streams in FFmpeg while stream copying codecs (-c copy) and
preserving original presentation timestamps using the
-copyts flag. When working with broadcast formats like
MPEG-TS or MXF, maintaining synchronization between video, audio, and
embedded or external closed captions requires explicit stream mapping
alongside timestamp preservation commands.
Understanding Closed Captions in FFmpeg
Closed captions typically exist in one of two formats within media files:
- Embedded Video User Data (CEA-608 / CEA-708):
Stored inside the video bitstream's SEI (Supplemental Enhancement
Information) NAL units or MPEG-2 user data. When you use
-c:v copy, these embedded captions are automatically preserved because the video bitstream is copied without decoding. - Dedicated Data or Subtitle Streams: Stored as
independent tracks within the container (common in MXF, ATSC MPEG-TS, or
MP4 files). These must be explicitly mapped to the output container
using FFmpeg's
-mapoption.
The Command Syntax
To preserve all streams (including dedicated subtitle and data streams), retain original timestamps, and avoid re-encoding, use the following command structure:
ffmpeg -copyts -i input.ts -map 0 -c copy -muxdelay 0 -muxpreload 0 output.tsTargeted Stream Mapping
If the input file contains unwanted tracks (such as multiple
languages or metadata tracks you wish to exclude), you should explicitly
map the video, audio, and caption streams rather than mapping the entire
file with -map 0.
ffmpeg -copyts -i input.mxf \
-map 0:v:0 \
-map 0:a \
-map 0:s? \
-map 0:d? \
-c copy \
output.mxfExplanation of Options:
-copyts: Directs FFmpeg not to reset timestamps to zero, preserving original presentation timestamps (PTS) and decoding timestamps (DTS). This prevents out-of-sync captions.-map 0:v:0: Selects the primary video stream. If captions are embedded in the video bitstream (A/53 / CEA-708), copying this stream copies the captions.-map 0:a: Selects all audio streams.-map 0:s?: Maps all subtitle streams if they exist. The trailing?prevents the command from failing if no subtitle streams are present.-map 0:d?: Maps all data streams. Some containers (like MXF) carry closed captions as SMPTE 436M data streams rather than traditional subtitle tracks.-c copy: Ensures all selected streams are copied without re-encoding, preserving caption integrity.
Handling
Timestamp Offsets with -start_at_zero
When using -copyts, containers may experience playback
issues if the source timestamps start with a massive offset (common in
live broadcast captures). To shift the timeline so the initial packet
starts at timestamp 0 while maintaining the internal offsets between
video and caption streams, add -start_at_zero:
ffmpeg -copyts -start_at_zero -i input.ts -map 0:v -map 0:a -map 0:s? -c copy output.tsVerification
After processing, verify that the caption streams and their packet counts were successfully transferred to the output container:
ffprobe -v error -show_streams -select_streams s output.tsFor embedded bitstream captions, verify CEA-608/708 presence using
the eia608 or readvitc filter:
ffmpeg -i output.ts -f lavfi -i "movie=output.ts[out0];[out0]select='eq(pict_type,I)',showinfo" -f null -