Media Gpac
Context: $ARGUMENTS
Quick start
- Inspect a broken MP4: → Step 2 (
info/diso) - Package DASH/CMAF: → Step 3 (
dashsubcommand) - Fragment for fMP4: → Step 3 (
fragment) - Extract a raw track (h264/aac ES): → Step 3 (
extract-track) - CENC encrypt for ClearKey testing: → Step 3 (
encrypt) - Hand commercial DRM to Shaka instead: →
media-shakaskill
When to use
- Doing ISO-BMFF surgery ffmpeg can't (box-level edits, precise track IDs, edit-list fixes).
- Packaging CMAF/DASH where fragment timing must be accurate (MP4Box beats ffmpeg's dash muxer).
- Dumping the full box tree to XML for diagnostics (
-diso). - Repairing broken moov/moof boxes, rewriting sidx, or setting language/default flags in-place.
- Authoring ClearKey-encrypted CENC content for pipeline testing.
Step 1 — Install
# macOS
brew install gpac
# Debian/Ubuntu
sudo apt install gpac
# Fedora/RHEL
sudo dnf install gpac
# Verify
MP4Box -version
gpac -version
MP4Box is the legacy (stable, widely documented) CLI for ISOBMFF surgery. gpac is the newer filter-graph CLI (similar philosophy to ffmpeg filters). Prefer MP4Box for most tasks — it's documented everywhere.
Step 2 — Pick the operation
| Task | Tool |
|---|---|
| Human-readable summary | MP4Box -info in.mp4 |
| Full box tree as XML | MP4Box -diso in.mp4 (writes in_info.xml) |
| Extract track as raw ES | MP4Box -raw TID in.mp4 |
| Fragment for fMP4 | MP4Box -frag 2000 in.mp4 |
| DASH / CMAF package | MP4Box -dash 4000 -frag 4000 -rap … |
| Inject sidx | MP4Box -add-sidx 4000 in.mp4 |
| CENC encrypt | MP4Box -crypt drm.xml -out o.mp4 in.mp4 |
| CENC decrypt | MP4Box -decrypt drm.xml -out o.mp4 in.mp4 |
| Remove track | MP4Box -rem TID in.mp4 |
| Set language | MP4Box -lang TID=jpn in.mp4 |
| Enable/disable | MP4Box -enable TID -disable TID in.mp4 |
| Split by time | MP4Box -splitx T1:T2 in.mp4 |
| Edit list | MP4Box -elst '0,N,1' in.mp4 |
| Add tracks | MP4Box -add a.h264 -add a.aac out.mp4 |
Always pass -out when you want a new file — MP4Box edits in-place otherwise. Back up first.
Step 3 — Run via the helper script
The scripts/gpac.py wrapper gives an argparse interface over these operations. Stdlib-only, non-interactive, --dry-run prints the command.
# Inspect
uv run ${CLAUDE_SKILL_DIR}/scripts/gpac.py info --input in.mp4
uv run ${CLAUDE_SKILL_DIR}/scripts/gpac.py diso --input in.mp4 --output boxes.xml
# Extract raw track 1 (ES)
uv run ${CLAUDE_SKILL_DIR}/scripts/gpac.py extract-track --input in.mp4 --track 1 --output track1.h264
# Fragment (2-second fragments, in-place copy with -out)
uv run ${CLAUDE_SKILL_DIR}/scripts/gpac.py fragment --input in.mp4 --output frag.mp4 --fragment-ms 2000
# DASH/CMAF (live profile)
uv run ${CLAUDE_SKILL_DIR}/scripts/gpac.py dash --input in.mp4 --outdir dashout --segment-ms 4000 --profile live
# CENC ClearKey encrypt
uv run ${CLAUDE_SKILL_DIR}/scripts/gpac.py encrypt \
--input in.mp4 --output enc.mp4 \
--key-id 0123456789abcdef0123456789abcdef \
--key fedcba9876543210fedcba9876543210
# Remove track 2 (usually audio #2)
uv run ${CLAUDE_SKILL_DIR}/scripts/gpac.py remove-track --input in.mp4 --output stripped.mp4 --track 2
# Set language on track 2
uv run ${CLAUDE_SKILL_DIR}/scripts/gpac.py set-lang --input in.mp4 --track 2 --lang jpn
# Split by time
uv run ${CLAUDE_SKILL_DIR}/scripts/gpac.py split-time --input in.mp4 --output-pattern seg --start 00:00:10 --end 00:00:30
Add --dry-run to print the MP4Box command without executing. Add --verbose to echo the command before running.
Step 4 — Verify
After any authoring step, confirm result with:
MP4Box -info out.mp4
For DASH, validate the manifest:
xmllint --noout manifest.mpd
ls -la dashout/ # should contain init.mp4 + seg_*.m4s (live) OR one onDemand file
For CENC: play with ffplay -decryption_key KEY enc.mp4 or a Shaka-Player ClearKey page. For fragmented MP4: MP4Box -info shows Fragmented: yes and lists moof count.
Available scripts
scripts/gpac.py— argparse wrapper for MP4Box:check,info,diso,extract-track,fragment,dash,encrypt,decrypt,remove-track,set-lang,split-time. Generates DRM XML for CENC ops in a temp file.
Reference docs
Read references/gpac.md when:
- Choosing a DASH profile (
live,onDemand,main,full). - Looking up an ISO-BMFF box (
moov,moof,sidx,senc,saio,saiz,pssh,mvex). - Writing a CENC XML by hand.
- Translating MP4Box idioms to the new
gpacfilter graph syntax.
Gotchas
MP4Boxvsgpac: both ship with the GPAC framework.MP4Boxis legacy CLI (stable, everywhere).gpac(lowercase) is the newer filter-graph CLI —gpac -i input -o outputwith a filter pipeline; list plugins viagpac -h filters. PreferMP4Boxunless you need filter-graph semantics.- In-place edits: MP4Box mutates the input file unless you pass
-out. Always back up or pass-out. - Track IDs start at 1, not 0.
MP4Box -infolists them. -raw TIDextracts the ES (elementary stream: raw H.264 NAL units, raw AAC ADTS, etc.) — not a container. Feed it back in via-add track.h264.-disodump is huge — dumps the entire box tree as XML. Redirect to a file; don't cat it.-dash 4000= 4000 ms segment duration.-fragis fragment duration inside each segment (usually equal to-dash).-rapforces each segment to start on a random access point (IDR). Omit and you will get unseekable DASH.sidx= Segment Index box. Required for byte-range DASH (onDemandprofile). Live profile uses separate.m4sfiles and doesn't need it.- CMAF segments are
.m4s+init.mp4. Sent as independent files over HTTP. -dash-profile live→ segmented, fragmented, templated URLs ($Number$).onDemand→ single file with indexed byte ranges.main/full= older DASH profiles.-dash-ctx <file>saves packaging state — required for continuous live DASH across MP4Box invocations.- CENC encryption XML follows a GPAC-specific schema (see references).
KIDandkeyare hex with0xprefix.IV_sizeis 8 (CTR-64) or 16 (CTR-full).first_IVseeds the counter. - MP4Box does CENC but NOT commercial DRM (Widevine / PlayReady / FairPlay license-server signaling). For those, package with MP4Box first, then hand off to Shaka Packager — see
media-shakaskill. sencvssaio/saiz: MP4Box writes the correct auxiliary-info boxes; don't mix CENC between tools mid-pipeline.- ffmpeg philosophy vs MP4Box: ffmpeg is general-purpose (many containers, filters, codecs). MP4Box is ISOBMFF-surgeon — precise box manipulation ffmpeg's demuxer/muxer can't express.
-tightreorders themoovbox for streaming (fast-start). Equivalent to ffmpeg's-movflags +faststart.- Disk space: MP4Box writes temp files next to the input. Large DASH packaging can briefly 2x disk use.
-splitx T1:T2uses timecodes (e.g.,00:01:00:00:02:00). Some versions want-splitx T1/T2syntax — checkMP4Box -h spliton your build.
Examples
Example 1: Package a mezzanine MP4 for DASH live
MP4Box -dash 4000 -frag 4000 -rap \
-segment-name 'seg_$RepresentationID$_$Number$' \
-dash-profile live \
-out dashout/manifest.mpd \
input.mp4#video input.mp4#audio
Result: dashout/manifest.mpd, per-representation init.mp4, and seg_video_1.m4s, seg_audio_1.m4s, ...
Example 2: Repair an MP4 that ffmpeg can't stream
# 1. Inspect: look for missing sidx, broken edit list, or non-fragmented moov
MP4Box -info broken.mp4
# 2. Rewrite with fragments + sidx
MP4Box -frag 2000 -add-sidx 2000 -out fixed.mp4 broken.mp4
# 3. Verify
MP4Box -info fixed.mp4 # "Fragmented: yes", moof count > 0, sidx present
Example 3: CENC ClearKey encrypt for pipeline testing
drm.xml:
<GPACDRM>
<CrypTrack trackID="1" IsEncrypted="1" IV_size="8"
first_IV="0x0123456789abcdef" saiSavedBox="senc">
<key KID="0xABCDEF01234567890ABCDEF012345678"
value="0x112233445566778899AABBCCDDEEFF00"/>
</CrypTrack>
</GPACDRM>
MP4Box -crypt drm.xml -out enc.mp4 in.mp4
Decrypt with the same XML: MP4Box -decrypt drm.xml -out dec.mp4 enc.mp4.
Example 4: Extract H.264 ES, remux into new MP4
MP4Box -raw 1 in.mp4 # writes in_track1.h264
MP4Box -raw 2 in.mp4 # writes in_track2.aac
MP4Box -add in_track1.h264 -add in_track2.aac -new combined.mp4
Troubleshooting
Error: Cannot find adaptation set
Cause: DASH packager couldn't correlate streams — usually bad #video/#audio track-selection syntax.
Solution: list tracks via MP4Box -info. Use input.mp4#trackID=1 explicit syntax instead of #video.
Error: Segment duration not a multiple of frame rate
Cause: -dash 4000 doesn't align to the GOP. With 24 fps + 2s GOP, pick 4000/8000 ms (multiples of GOP ms).
Solution: re-encode source with a fixed GOP matching your segment duration, or use -rap and accept slightly variable segment sizes.
Error: Cannot write to file: permission denied
Cause: MP4Box tries to edit input in-place and the file is read-only.
Solution: pass -out newname.mp4 explicitly.
Error: Unknown track reference
Cause: operating on a track ID that doesn't exist.
Solution: MP4Box -info file.mp4 — track IDs are 1-based and listed in the summary.
Error: Encryption: wrong KID/key format
Cause: hex values missing 0x prefix or wrong length (KID must be 16 bytes / 32 hex chars; key must be 16 bytes / 32 hex chars).
Solution: double-check lengths. openssl rand -hex 16 generates valid values.