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:

  1. 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.
  2. 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 -map option.

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.ts

Targeted 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.mxf

Explanation 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.ts

Verification

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.ts

For 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 -