ffmpeg-mxf-imf
Context: $ARGUMENTS
Quick start
- Make an OP1a broadcast MXF (DNxHD/DNxHR): → Step 3 recipe A.
- Make an OP-Atom Avid-style MXF (one essence per file): → Step 3 recipe B.
- ProRes .mov mezzanine: → Step 3 recipe C.
- XDCAM HD422 50 Mbps MXF: → Step 3 recipe D.
- Author an IMF package: cannot be done with ffmpeg alone → Step 3 recipe E.
- Stamp SMPTE timecode (DF vs NDF): → Step 3 recipe F.
When to use
- Broadcast exchange delivery (MXF OP1a to a TV network QC dept).
- Avid ingest / export (MXF OP-Atom).
- Studio / streamer mastering (ProRes mezzanine, IMF delivery).
- Any time the user says "MXF", "IMF", "IMP", "XDCAM", "DNxHD", "ProRes", "SMPTE", "broadcast deliverable", or "Netflix spec".
- Embedding or restamping SMPTE timecode on a mastering track.
- Reading SMPTE 2022 / 2110 IP essences (niche, build-dependent).
Step 1 — Identify the target spec
Ask / infer:
- Container: MXF OP1a? MXF OP-Atom? MOV (ProRes)? IMF? GXF?
- Codec: DNxHD, DNxHR, XDCAM HD422, ProRes 422 HQ / 4444, JPEG 2000, etc.
- Timecode: drop-frame or non-drop-frame? Starting at
01:00:00:00? - Audio: PCM 24-bit 48 kHz is the broadcast default. How many tracks / channels?
- Interlacing:
setfield=tfffor broadcast TFF output? - IMF CPL: if IMF, ffmpeg alone is not enough — plan for
asdcplib/photon.
Step 2 — Pick encoder + muxer
| Target | Codec | Muxer |
|---|---|---|
| MXF OP1a broadcast | dnxhd / dnxhr |
-f mxf |
| MXF OP-Atom (Avid/IMF) | dnxhd / dnxhr / prores |
-f mxf_opatom |
| MXF D-10 IMX | mpeg2video D-10 |
-f mxf_d10 |
| XDCAM HD422 | mpeg2video 50 Mb CBR |
-f mxf |
| ProRes mezzanine | prores_ks |
-f mov |
| GXF (Grass Valley) | mpeg2video / others |
-f gxf |
See references/formats.md for full codec profile tables.
Step 3 — Run
A. MXF OP1a, DNxHD 120 Mbps, PCM 24-bit (broadcast default)
ffmpeg -i in.mov \
-c:v dnxhd -b:v 120M -pix_fmt yuv422p \
-vf "setfield=tff" \
-c:a pcm_s24le -ar 48000 \
-timecode "01:00:00:00" \
-f mxf out_op1a.mxf
For DNxHR (raster-independent successor): -c:v dnxhd -profile:v dnxhr_hq -pix_fmt yuv422p.
B. MXF OP-Atom (one essence per file, Avid/IMF style)
ffmpeg -i in.mov \
-map 0:v -c:v dnxhd -b:v 120M -pix_fmt yuv422p \
-f mxf_opatom video.mxf
ffmpeg -i in.mov \
-map 0:a:0 -c:a pcm_s24le -ar 48000 -f mxf_opatom audio_1.mxf
One file per video essence + one file per audio channel. Script mxf-opatom does the fan-out.
C. ProRes 422 HQ mezzanine (most common intermediate)
ffmpeg -i in.mov \
-c:v prores_ks -profile:v 3 -pix_fmt yuv422p10le -vendor apl0 \
-c:a pcm_s24le \
out_prores.mov
Profile map: 0 Proxy, 1 LT, 2 Standard 422, 3 HQ 422, 4 4444, 5 4444 XQ.
D. XDCAM HD422 50 Mbps CBR MXF
ffmpeg -i in.mov \
-c:v mpeg2video -pix_fmt yuv422p \
-b:v 50M -maxrate 50M -minrate 50M -bufsize 17M \
-flags +ildct+ilme -top 1 \
-c:a pcm_s24le -ar 48000 \
-f mxf out_xdcam.mxf
E. IMF package (read carefully)
ffmpeg cannot author an IMF package. IMF = MXF essence files + XML CPL (Composition Playlist), PKL, ASSETMAP, OPL, VOLINDEX. ffmpeg writes the MXF essence (J2K SMPTE ST 2067-20 or ProRes ST 2067-21); CPL authoring requires external tooling:
- Netflix Photon (
https://github.com/Netflix/photon) — validates IMF packages. - asdcplib / asdcp-wrap — creates the J2K MXF essence for IMF.
- IMF Conversion Utility (Netflix) — converts ProRes/etc. into IMF.
- OpenTimelineIO + Tractor — can stitch CPLs.
Document this limitation; do not pretend ffmpeg alone produces an IMP.
F. Stamp SMPTE timecode
# Non-drop-frame (24, 25, 30, 50, 60 fps) — use COLON
ffmpeg -i in.mov -c copy -timecode "01:00:00:00" -f mxf out.mxf
# Drop-frame (29.97, 59.94) — use SEMICOLON before frames field
ffmpeg -i in.mov -c copy -timecode "01:00:00;00" -f mxf out.mxf
The semicolon vs. colon is load-bearing — it is how ffmpeg selects DF.
Step 4 — Verify
ffprobe -v error -show_streams -show_format out.mxf
ffprobe -v error -show_entries format_tags:stream_tags=timecode out.mxf
mediainfo --Output=JSON --Full out.mxf # deeper: GOP, profile, level
Look for: codec_name, pix_fmt, r_frame_rate, tags.timecode, MXF Operational Pattern in mediainfo. IMF essence should show JPEG 2000 or ProRes with 10-bit 4:2:2+ and a UL identifying SMPTE ST 2067.
Available scripts
scripts/mxfimf.py— subcommands:mxf-op1a,mxf-opatom,prores-mov,xdcam-hd422,set-timecode,identify-imf.
uv run ${CLAUDE_SKILL_DIR}/scripts/mxfimf.py mxf-op1a \
--input in.mov --output out.mxf --codec dnxhd --timecode "01:00:00:00"
uv run ${CLAUDE_SKILL_DIR}/scripts/mxfimf.py set-timecode \
--input in.mov --output out.mov --tc "01:00:00;00" --drop-frame
uv run ${CLAUDE_SKILL_DIR}/scripts/mxfimf.py identify-imf --input suspect.mxf
All subcommands accept --dry-run and --verbose.
Reference docs
- Read
references/formats.mdfor MXF operational patterns, DNxHD/DNxHR profiles, ProRes profiles, XDCAM variants, timecode syntax, IMF spec refs, Netflix/Disney/Apple delivery notes, external IMF tooling, SMPTE 2022/2110 overview, and DeckLink SDI capture.
Gotchas
- MXF Operational Patterns differ.
-f mxf= OP1a (single file, broadcast default).-f mxf_opatom= OP-Atom (one essence per file, Avid + IMF).-f mxf_d10= D-10 / IMX. Confirm which the downstream system requires — mixing them up causes silent ingest rejection. - Drop-frame timecode syntax uses SEMICOLON.
01:00:00;00= drop-frame.01:00:00:00= non-drop-frame. 29.97 fps NDF is not the same as 29.97 DF — NDF never skips, DF skips 2 frames every minute except every 10th minute. - Interlacing flags: broadcast TFF needs
-vf setfield=tffon output, plus-flags +ildct+ilme -top 1for MPEG-2. Missing these produces ingest-rejecting files. - PCM is standard for MXF audio. Use
pcm_s24leat 48 kHz. Don't use AAC in broadcast MXF. - OP-Atom = one channel per file. Each logical audio channel typically becomes its own MXF file. The
mxf-opatomsubcommand fans out automatically. -mpegts_flags system_bfor broadcast MPEG-TS compliance.- IMF delivery to Netflix requires specific codec profiles: JPEG 2000 (SMPTE ST 2067-20) or ProRes IMF (SMPTE ST 2067-21). See Netflix Partner Help Center for the current delivery spec; ffmpeg alone cannot author the XML CPL — use Netflix Photon or asdcplib.
- HDR metadata in MXF (Dolby Vision RPU, HDR10 static) needs specific handling: mux-time metadata via
-metadatararely suffices; Dolby Vision usually requires MP4Box ordovi_toolafter encode. - Timecode source: input side use
-metadata timecode=HH:MM:SS:FF, output side use-timecode— they are different flags. - SMPTE 2110 needs a build with 2110 SDP support plus PTP / IEEE 1588 network time sync in production. Most stock ffmpeg builds do not include it.
- SDI capture via Blackmagic DeckLink:
-f decklink -i "DeckLink Mini Recorder". Requires the Blackmagic Desktop Video driver. - MPEG-2 broadcast grade: add
-mbd rd -trellis 2 -cmp 2 -subcmp 2for better RD. - Never use H.264 Baseline for broadcast — use Main or High. Baseline lacks B-frames / CABAC and fails most broadcast conformance suites.
_originalsuffix on output means ffmpeg refused to overwrite — pass-yto force overwrite.- XDCAM EX has specific GOP and quality ceilings (35 Mbps) different from HD422 (50 Mbps). Do not use HD422 params to target XDCAM EX.
Examples
Example 1: OP1a broadcast deliverable with timecode
Input: master.mov (ProRes 422 HQ, 29.97 fps, needs DF timecode).
uv run ${CLAUDE_SKILL_DIR}/scripts/mxfimf.py mxf-op1a \
--input master.mov --output deliverable.mxf \
--codec dnxhd --timecode "10:00:00;00"
ffprobe -show_entries stream_tags=timecode deliverable.mxf
Result: OP1a MXF with DNxHD 120 Mbps video + PCM 24-bit audio + drop-frame SMPTE timecode starting at 10:00:00;00.
Example 2: Avid OP-Atom fan-out
uv run ${CLAUDE_SKILL_DIR}/scripts/mxfimf.py mxf-opatom \
--input master.mov --outdir /tmp/opatom --codec dnxhr
Produces video.mxf, audio_0.mxf, audio_1.mxf, … one essence per file.
Example 3: IMF identification
uv run ${CLAUDE_SKILL_DIR}/scripts/mxfimf.py identify-imf --input unknown.mxf
Reports whether the MXF uses SMPTE ST 2067 descriptors and whether ffmpeg can round-trip it. If IMF, instructs user to fetch the CPL and use Netflix Photon.
Troubleshooting
Error: Could not find tag for codec ... in stream #0, codec not currently supported in container
Cause: codec/muxer mismatch (e.g., H.264 in mxf_opatom, or AAC in MXF).
Solution: MXF wants DNxHD/DNxHR/MPEG-2/JPEG 2000 for video and PCM for audio. Re-encode with a supported codec.
Error: OP-Atom output has multiple streams
Cause: mxf_opatom supports exactly one video or one audio essence per file.
Solution: split with -map and produce one MXF per essence. Use the mxf-opatom subcommand.
Timecode shows 00:00:00:00 after mux
Cause: -timecode must be on the output side, not input; or input was -c copy without the output flag.
Solution: put -timecode "HH:MM:SS:FF" as an output option, after -i. For DF, use semicolon.
Broadcaster rejects the file for interlace non-compliance
Cause: missing setfield=tff and/or -top 1, or wrong field order.
Solution: add -vf setfield=tff -flags +ildct+ilme -top 1 for TFF broadcast.
Output MXF has _original suffix
Cause: a file with that name already existed and ffmpeg refused to overwrite.
Solution: add -y to the ffmpeg command, or delete the existing output first.
IMF package validation fails in Netflix Photon
Cause: ffmpeg does not author CPLs. You produced the essence but not the XML package.
Solution: use asdcp-wrap or the Netflix IMF Conversion Utility to wrap the essence and generate CPL/PKL/ASSETMAP. ffmpeg is only step 1.