How to QC-decode an SCC or MCC caption file with Caption Inspector
💡 Run
docker run --rm -v $(pwd):/data nebulabroadcast/caption-inspector -o /data /data/my-captions.scc -f 2997to independently decode any SCC or MCC file. It writes three output files: a human-readable CEA-608 transcript ending in-C1.608, a CEA-708 service decode ending in-S1.708, and a raw hex dump ending in.ccd. Read the.608file first - if text is garbled or PAC row codes are wrong, fix the source file before delivering to a broadcaster.
Key takeaways
- Caption Inspector (Comcast, open source) is the best free independent QC decoder for SCC and MCC broadcast caption files.
- Three outputs from one command:
-C1.608(readable 608 transcript with PAC row positions),-S1.708(708 window and service decode),.ccd(annotated byte-level hex dump). - Always pass
-f 2997for 29.97 fps content; omitting the framerate flag produces incorrect timecode in the output. Xin thecc_validcolumn of the.ccdfile marks an invalid byte pair - a broadcast decoder discards these.- SCC input also produces a
.mccsidecar, but that file carries the 608 compatibility bytes only, not a native CEA-708 service layer.
Why run a QC decode on every broadcast caption file?
A CEA-608 file (SCC) can pass format validation and still deliver wrong or empty captions to a viewer. The proprietary byte encoding means a quiet conversion error produces a file that opens in Subtitle Edit without complaint, but decodes to garbled text or wrong row positions on a broadcast decoder. I learned this on a QC pass for a client's SCC file: the text looked fine in Subtitle Edit's preview, but Caption Inspector showed all captions sitting at Row 1 instead of Row 15, meaning they would display at the top of every screen in the broadcast chain.
Caption Inspector reads the raw cc_data() structure and produces a plain-text decode, independent of any editing tool's interpretation. It is the same sanity check as compiling code: the tool that wrote the file will often preview its own errors correctly, but the target environment will not. I run it after every Subtitle Edit export before I zip a file for delivery.
The one-command QC decode
Pull the image once, then run it from the folder that contains your caption file. The -v flag mounts that folder into the container so output files land alongside the input.
docker run --rm -v $(pwd):/data nebulabroadcast/caption-inspector \
-o /data /data/my-captions.scc -f 2997
--rm: delete the container after it exits, no manual cleanup needed.-v $(pwd):/data: mount the current folder as/datainside the container.-o /data: write all output files to/data, which maps to your current folder on disk.-f 2997: set frame rate to 29.97 fps, the standard rate for US broadcast SCC files. Use-f 2400for 24 fps content. Without this flag, timecode arithmetic in the output is wrong.
The same command works for MCC files and for captions embedded in MPEG-2 (.ts, .mpg) and MPEG-4 (.mp4) containers. For an MXF source, use CCExtractor first to pull an SCC, then run Caption Inspector on that SCC. To confirm a video container has caption data before decoding, run ffprobe:
ffprobe -v quiet -show_streams -select_streams d input.ts
What do the three output files tell you?
| Output suffix | Contents | Read first? |
|---|---|---|
-C1.608 | Human-readable CEA-608 Channel 1 decode: text in quotes, timestamps, Global Control Codes in {}, PAC row and column positions | Yes - primary text check |
-C2.608 | Same for Channel 2 (CC2 stream) | If Channel 1 is empty or garbled |
-S1.708 | CEA-708 Service 1 decode: window commands (Define Window, Set Pen Location), text, pen and color attributes | For MCC files - confirm 708 carries real text |
.ccd | Annotated hex dump of every frame's cc_data() bytes with cc_valid markers and decoded text alongside | When the .608 looks wrong - trace the bytes here |
.mcc (SCC only) | 608 bytes re-wrapped in an MCC container (compatibility layer, not a native 708 service) | Only if you need an MCC handoff - re-verify before using |
What should I look for in the decoded output?
Open the -C1.608 file in any text editor. A clean pop-on caption sequence looks like this:
00:00:08;22
{Resume Caption Loading}
{Preamble Address Code: Row 15, Column 0, White, Normal}
"Hello, and welcome to the program."
{End of Caption}
00:00:11;04
{Resume Caption Loading}
{Preamble Address Code: Row 14, Column 0, White, Normal}
"This is the second caption event."
{End of Caption}
Four things I check first in the .608 file:
- Is Channel 1 populated? If
-C1.608is empty or full of garbage, check-C2.608instead. Some authoring tools default to writing CC2. Most US broadcast deliverables require the primary captions on CC1. - Does the text match the script? Any replacement characters or unreadable sequences signal a character encoding failure. CEA-608 supports only Latin-script characters - if the source contained characters outside that set, they will appear as garbage in the decoded output.
- Are the PAC rows correct? Row 15 is the bottom of the 15-row CEA-608 grid; Row 1 is the top. If all captions are at Row 1 when the script calls for bottom-of-screen placement, a conversion step dropped the positioning data and substituted a default.
- Does the timecode use semicolons?
00:00:08;22(semicolon before the frame count) is drop-frame timecode, correct for 29.97 fps NTSC content. Colons instead of semicolons indicate non-drop-frame, a mismatch that causes captions to drift over long programmes.
Reading the .ccd hex dump
When the .608 text looks wrong, open the .ccd file to trace the raw bytes. Each frame entry looks like:
00:00:08;22
{F1:94 20} {Global Control Code: Resume Caption Loading}
{F1:d6 e5} {PAC Row 15, Col 0, White} "He"
The validity marker is the key field to check:
F: valid Line 21 / Field 1 byte pair (CEA-608 data). This is what you want to see in a caption-carrying frame.P: valid DTVCC / CEA-708 packet byte.X: invalid byte pair. A broadcast decoder discards these. Even a short run ofXentries in caption-carrying frames means the encoder wrote bytes the decoder cannot read.
What you can do yourself vs when a specialist is worth it
This entire workflow runs on any machine with Docker installed. Pull the image once (about 50 MB), and a QC decode on a single file finishes in under 10 seconds. For regular broadcast deliverables, a two-line shell script that runs Caption Inspector automatically after each Subtitle Edit export catches encoding errors before they reach a broadcaster's ingestion system.
Where experience makes a difference: if your .608 decode shows errors you cannot trace back to a specific conversion step, or if your broadcaster's spec requires a verified native CEA-708 service layer, those are cases where the authoring chain matters.
The .mcc sidecar Caption Inspector generates from an SCC input carries the 608 bytes in a 708 container (the compatibility layer), but it is not a native 708 service with defined windows, pen attributes, and color. Some broadcaster QC systems pass it; others do not. For a deliverable that must carry a real 708 service, the correct path is a SMPTE-TT (ST 2052-1) XML master. My caption QC service covers the decode check and 708 service validation for US broadcast and streaming deliverables.
FAQ
Can I run Caption Inspector without Docker on Windows?
The prebuilt binary requires Linux libraries, so Docker is the most practical path on Windows. The GitHub repository at github.com/Comcast/caption-inspector includes build instructions for native compilation, but you need FFMPEG 4.0.2 or later and optionally MediaInfo CLI. Docker Desktop on Windows is simpler in practice.
What is the difference between Channel 1 and Channel 2 in CEA-608?
CEA-608 Field 1 carries two independent caption channels. CC1 (Channel 1) is the primary stream for most US broadcast content. CC2 (Channel 2) carries a secondary stream, sometimes a second language or an alternate audio description track. Caption Inspector writes each to its own file: -C1.608 for CC1 and -C2.608 for CC2. Most US broadcast specs require the primary captions on CC1.
Why does my .608 file contain random characters I did not write?
Caption Inspector's documentation notes that channels may contain "random garbage" that does not represent valid captions. CEA-608 carries null-filler bytes on every field of every frame, and those bytes can decode to meaningless character combinations. Valid caption data is anchored by Global Control Codes such as Resume Caption Loading and End of Caption. Garbage text with no surrounding control codes means the channel is carrying null filler, not real captions.
Is the .mcc sidecar Caption Inspector generates usable as a broadcast MCC deliverable?
Use it with caution. The .mcc from an SCC input wraps the 608 bytes in an MCC container - it carries the CEA-608 compatibility layer of a 708 file, not a native 708 service. Some broadcasters and QC tools accept this; others specifically validate that the 708 service carries properly defined windows, pen attributes, and colors. If your spec requires a verified 708 service, start from a SMPTE-TT (ST 2052-1) XML master and do not rely on the SCC-to-MCC conversion from Caption Inspector.
Does Caption Inspector work on MXF files?
Not directly with the Docker image. The README states that MOV support requires the GPAC library compiled in, and MXF is not listed among supported input containers. For captions in an MXF, use CCExtractor first to extract an SCC file, then run Caption Inspector on the extracted SCC for the decode check.
Official Sources
- Comcast/caption-inspector - GitHub - README, command syntax, output format documentation (decoded608.md, decoded708.md, decodeCCD.md). Verified Oct 2026.
- nebulabroadcast/caption-inspector - Docker Hub - public Docker image for Caption Inspector. Verified Oct 2026.
- Caption Inspector decoded608.md - CEA-608 output file format: field layout, PAC codes, control codes. Verified Oct 2026.
- Caption Inspector decodeCCD.md - CCD hex dump format: cc_valid and cc_type fields, hex structure. Verified Oct 2026.
- CCExtractor - GitHub - open-source caption extraction tool for TS, MP4, and MXF containers. Verified Oct 2026.
Written by Dao Huy (Lucas), Vietnamese translator & localization specialist (EN · ZH · FR → Vietnamese). See translation services →
