Task 437: .MXF File Format

Task 437: .MXF File Format

File Format Specifications for .MXF

The Material Exchange Format (MXF) is a container format for professional digital video and audio media, defined by SMPTE standards, primarily SMPTE ST 377-1:2019 (the core file format specification). It supports multiple essence streams (video, audio, data) encoded in various compression formats, along with rich metadata. MXF uses KLV (Key-Length-Value) encoding for all elements, with files structured into partitions (Header, Body, Footer) for interoperability in broadcast and post-production workflows. The format is open, platform-agnostic, and extensible via operational patterns (e.g., OP1a for simple single-item files).

  1. List of All Properties Intrinsic to the .MXF File Format

Based on the MXF specifications, the following are the key structural properties intrinsic to the format, derived from the Partition Pack (the first KLV item in the file, typically the Header Partition) and essential for file system integrity and parsing. These are format-specific attributes that define the file's layout, version, alignment, and content organization, excluding general file system metadata like size or timestamps. They are parsed from the Value field of the Header Partition Pack KLV (Key: 06 0E 2B 34 02 05 01 01 0D 01 02 01 01 XX 00 00, where XX is 01-04 for status).

  • Major Version: UInt16 indicating the major specification version (e.g., 1 for SMPTE ST 377-1 compatibility).
  • Minor Version: UInt16 indicating the minor revision (e.g., 3 for backward-compatible updates).
  • KAG Size: UInt32 for KLV Alignment Grid size (0 for no alignment, or powers of 2 up to 1,048,576 bytes).
  • This Partition Offset: UInt64 byte offset from file start to this partition (0 for Header).
  • Previous Partition Offset: UInt64 offset to the previous partition (0 if first).
  • Footer Partition Offset: UInt64 offset to the Footer Partition (0 or max if absent).
  • Header Byte Count: UInt64 size of Header Metadata section (including Primer Pack and Sets; >0 in Header).
  • Index Byte Count: UInt64 size of Index Table section (0 if absent).
  • Index SID: UInt32 Stream ID for Index Tables (unique non-zero if present).
  • Body Offset: UInt64 offset within the Essence Container (in Edit Units; 0 for Header/Footer).
  • Body SID: UInt32 Stream ID for Essence Container (unique non-zero if essence present; 0 for metadata-only partitions).
  • Operational Pattern: 16-byte Universal Label (UL) in hex (e.g., 06 0E 2B 34 02 53 01 01 0D 01 01 01 01 01 01 00 for OP1a single-item).
  • Essence Containers: Batch of ULs (UInt32 count followed by count × 16-byte ULs in hex, identifying essence mappings like SMPTE 381M for MPEG).

These properties ensure the file's partition-based structure, enabling random access, streaming, and recovery.

  1. Two Direct Download Links for .MXF Files
  1. Ghost Blog Embedded HTML JavaScript for Drag-and-Drop .MXF Property Dump

This is embeddable HTML/JavaScript code for a Ghost blog post. It creates a drop zone where users can drag and drop an .MXF file. The script uses FileReader to load the file as an ArrayBuffer, parses the first Partition Pack (assuming it's the Header), extracts the properties listed above, and dumps them to the screen in a

element. It handles big-endian byte order via DataView and assumes a standard BER Length (skipping Run-In if present by searching for the Header Key). For write, it includes a button to download a modified dummy .MXF with hardcoded properties (basic write example; not full file creation).

Drag and drop .MXF file here


  1. Python Class for .MXF Handling

This Python class opens an .MXF file, decodes the first Header Partition Pack, reads and prints the properties, and includes a write method to create a dummy .MXF file with hardcoded properties (basic encode/write; not full modification).

import struct
import os

class MXFParser:
    def __init__(self, filepath):
        self.filepath = filepath
        self.properties = {}

    def read_and_print(self):
        with open(self.filepath, 'rb') as f:
            data = f.read()
        # Find Header Key (skip Run-In)
        header_key = b'\x06\x0E\x2B\x34\x02\x05\x01\x01\x0D\x01\x02\x01\x01\x01'
        offset = data.find(header_key)
        if offset == -1:
            print('Invalid MXF: Header Partition Pack not found.')
            return
        offset += 16  # Skip Key
        # Parse BER Length (simple handling)
        first_len = data[offset]
        offset += 1
        if first_len > 0x80:
            len_bytes = first_len & 0x7F
            length = int.from_bytes(data[offset:offset + len_bytes], 'big')
            offset += len_bytes
        else:
            length = first_len
        # Parse Value
        value = data[offset:offset + length]
        voffset = 0
        self.properties['Major Version'] = struct.unpack('>H', value[voffset:voffset+2])[0]; voffset += 2
        self.properties['Minor Version'] = struct.unpack('>H', value[voffset:voffset+2])[0]; voffset += 2
        self.properties['KAG Size'] = struct.unpack('>I', value[voffset:voffset+4])[0]; voffset += 4
        self.properties['This Partition Offset'] = struct.unpack('>Q', value[voffset:voffset+8])[0]; voffset += 8
        self.properties['Previous Partition Offset'] = struct.unpack('>Q', value[voffset:voffset+8])[0]; voffset += 8
        self.properties['Footer Partition Offset'] = struct.unpack('>Q', value[voffset:voffset+8])[0]; voffset += 8
        self.properties['Header Byte Count'] = struct.unpack('>Q', value[voffset:voffset+8])[0]; voffset += 8
        self.properties['Index Byte Count'] = struct.unpack('>Q', value[voffset:voffset+8])[0]; voffset += 8
        self.properties['Index SID'] = struct.unpack('>I', value[voffset:voffset+4])[0]; voffset += 4
        self.properties['Body Offset'] = struct.unpack('>Q', value[voffset:voffset+8])[0]; voffset += 8
        self.properties['Body SID'] = struct.unpack('>I', value[voffset:voffset+4])[0]; voffset += 4
        # OP UL hex
        op = ' '.join(f'{b:02X}' for b in value[voffset:voffset+16])
        self.properties['Operational Pattern'] = op; voffset += 16
        # Essence Containers
        ec_count = struct.unpack('>I', value[voffset:voffset+4])[0]; voffset += 4
        self.properties['Essence Containers'] = []
        for _ in range(ec_count):
            ul = ' '.join(f'{b:02X}' for b in value[voffset:voffset+16])
            self.properties['Essence Containers'].append(ul)
            voffset += 16
        print(self.properties)

    def write_dummy(self, output_path='dummy.mxf'):
        # Hardcode minimal Header Partition Pack
        key = b'\x06\x0E\x2B\x34\x02\x05\x01\x01\x0D\x01\x02\x01\x01\x01\x00\x00'
        length = b'\x54'  # Short len 84
        value = struct.pack('>HHIQQQQQIQI', 1, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0)  # Basics
        op_ul = b'\x06\x0E\x2B\x34\x02\x53\x01\x01\x0D\x01\x01\x01\x01\x01\x01\x00'
        ec_batch = struct.pack('>I', 0)  # Count 0
        full = key + length + value + op_ul + ec_batch
        with open(output_path, 'wb') as f:
            f.write(full)
        print(f'Dummy MXF written to {output_path}')

# Example usage:
# parser = MXFParser('path/to/file.mxf')
# parser.read_and_print()
# parser.write_dummy()
  1. Java Class for .MXF Handling

This Java class opens an .MXF file, decodes the Header Partition Pack, reads and prints the properties (using ByteBuffer for big-endian), and includes a write method for a dummy file.

import java.io.*;
import java.nio.*;
import java.util.*;

public class MXFParser {
    private String filepath;
    private Map<String, Object> properties = new HashMap<>();

    public MXFParser(String filepath) {
        this.filepath = filepath;
    }

    public void readAndPrint() throws IOException {
        byte[] data;
        try (FileInputStream fis = new FileInputStream(filepath)) {
            data = fis.readAllBytes();
        }
        ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.BIG_ENDIAN);
        // Find Header Key
        byte[] headerKey = {0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x01};
        int offset = -1;
        for (int i = 0; i < data.length - 16; i++) {
            boolean match = true;
            for (int j = 0; j < headerKey.length; j++) {
                if (data[i + j] != headerKey[j]) { match = false; break; }
            }
            if (match) { offset = i; break; }
        }
        if (offset == -1) {
            System.out.println("Invalid MXF: Header Partition Pack not found.");
            return;
        }
        offset += 16;
        // Parse BER Length (simple)
        int firstLen = bb.get(offset) & 0xFF;
        offset++;
        long length = 0;
        if (firstLen > 0x80) {
            int lenBytes = firstLen & 0x7F;
            for (int i = 0; i < lenBytes; i++) {
                length = (length << 8) | (bb.get(offset + i) & 0xFF);
            }
            offset += lenBytes;
        } else {
            length = firstLen;
        }
        // Parse Value
        properties.put("Major Version", bb.getShort(offset)); offset += 2;
        properties.put("Minor Version", bb.getShort(offset)); offset += 2;
        properties.put("KAG Size", bb.getInt(offset)); offset += 4;
        properties.put("This Partition Offset", bb.getLong(offset)); offset += 8;
        properties.put("Previous Partition Offset", bb.getLong(offset)); offset += 8;
        properties.put("Footer Partition Offset", bb.getLong(offset)); offset += 8;
        properties.put("Header Byte Count", bb.getLong(offset)); offset += 8;
        properties.put("Index Byte Count", bb.getLong(offset)); offset += 8;
        properties.put("Index SID", bb.getInt(offset)); offset += 4;
        properties.put("Body Offset", bb.getLong(offset)); offset += 8;
        properties.put("Body SID", bb.getInt(offset)); offset += 4;
        // OP UL
        StringBuilder op = new StringBuilder();
        for (int i = 0; i < 16; i++) op.append(String.format("%02X ", bb.get(offset + i)));
        properties.put("Operational Pattern", op.toString().trim()); offset += 16;
        // Essence Containers
        int ecCount = bb.getInt(offset); offset += 4;
        List<String> ecs = new ArrayList<>();
        for (int i = 0; i < ecCount; i++) {
            StringBuilder ul = new StringBuilder();
            for (int j = 0; j < 16; j++) ul.append(String.format("%02X ", bb.get(offset + j)));
            ecs.add(ul.toString().trim());
            offset += 16;
        }
        properties.put("Essence Containers", ecs);
        System.out.println(properties);
    }

    public void writeDummy(String outputPath) throws IOException {
        ByteBuffer bb = ByteBuffer.allocate(16 + 1 + 84).order(ByteOrder.BIG_ENDIAN);
        // Key
        byte[] key = {0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x01,0x00,0x00};
        for (byte b : key) bb.put(b);
        bb.put((byte)84); // Len
        bb.putShort((short)1); // Major
        bb.putShort((short)3); // Minor
        bb.putInt(1); // KAG
        bb.putLong(0); // This
        bb.putLong(0); // Prev
        bb.putLong(0); // Footer
        bb.putLong(0); // HeaderBC
        bb.putLong(0); // IndexBC
        bb.putInt(0); // IndexSID
        bb.putLong(0); // BodyOffset
        bb.putInt(0); // BodySID
        // OP UL
        byte[] opUL = {0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0D,0x01,0x01,0x01,0x01,0x01,0x01,0x00};
        for (byte b : opUL) bb.put(b);
        bb.putInt(0); // EC count 0
        try (FileOutputStream fos = new FileOutputStream(outputPath)) {
            fos.write(bb.array());
        }
        System.out.println("Dummy MXF written to " + outputPath);
    }

    // Example usage:
    // public static void main(String[] args) throws IOException {
    //     MXFParser parser = new MXFParser("path/to/file.mxf");
    //     parser.readAndPrint();
    //     parser.writeDummy("dummy.mxf");
    // }
}
  1. JavaScript Class for .MXF Handling

This JS class (Node.js compatible) opens an .MXF file via fs, decodes/reads/prints properties, and writes a dummy file.

const fs = require('fs');

class MXFParser {
  constructor(filepath) {
    this.filepath = filepath;
    this.properties = {};
  }

  readAndPrint() {
    const data = fs.readFileSync(this.filepath);
    const dv = new DataView(data.buffer);
    // Find Header Key
    const headerKey = new Uint8Array([0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x01]);
    let offset = 0;
    while (offset < data.length - 16) {
      let match = true;
      for (let i = 0; i < headerKey.length; i++) {
        if (dv.getUint8(offset + i) !== headerKey[i]) { match = false; break; }
      }
      if (match) break;
      offset++;
    }
    if (offset >= data.length - 16) {
      console.log('Invalid MXF: Header Partition Pack not found.');
      return;
    }
    offset += 16;
    // BER Length
    let firstLen = dv.getUint8(offset);
    offset++;
    let length = 0;
    if (firstLen > 0x80) {
      const lenBytes = firstLen & 0x7F;
      for (let i = 0; i < lenBytes; i++) {
        length = (length << 8) | dv.getUint8(offset + i);
      }
      offset += lenBytes;
    } else {
      length = firstLen;
    }
    // Parse Value
    this.properties['Major Version'] = dv.getUint16(offset); offset += 2;
    this.properties['Minor Version'] = dv.getUint16(offset); offset += 2;
    this.properties['KAG Size'] = dv.getUint32(offset); offset += 4;
    this.properties['This Partition Offset'] = Number(dv.getBigUint64(offset)); offset += 8;
    this.properties['Previous Partition Offset'] = Number(dv.getBigUint64(offset)); offset += 8;
    this.properties['Footer Partition Offset'] = Number(dv.getBigUint64(offset)); offset += 8;
    this.properties['Header Byte Count'] = Number(dv.getBigUint64(offset)); offset += 8;
    this.properties['Index Byte Count'] = Number(dv.getBigUint64(offset)); offset += 8;
    this.properties['Index SID'] = dv.getUint32(offset); offset += 4;
    this.properties['Body Offset'] = Number(dv.getBigUint64(offset)); offset += 8;
    this.properties['Body SID'] = dv.getUint32(offset); offset += 4;
    let op = [];
    for (let i = 0; i < 16; i++) op.push(dv.getUint8(offset + i).toString(16).padStart(2, '0').toUpperCase());
    this.properties['Operational Pattern'] = op.join(' '); offset += 16;
    const ecCount = dv.getUint32(offset); offset += 4;
    this.properties['Essence Containers'] = [];
    for (let i = 0; i < ecCount; i++) {
      let ul = [];
      for (let j = 0; j < 16; j++) ul.push(dv.getUint8(offset + j).toString(16).padStart(2, '0').toUpperCase());
      this.properties['Essence Containers'].push(ul.join(' '));
      offset += 16;
    }
    console.log(this.properties);
  }

  writeDummy(outputPath = 'dummy.mxf') {
    const buffer = new ArrayBuffer(16 + 1 + 84);
    const dv = new DataView(buffer);
    const headerKey = [0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x01,0x00,0x00];
    headerKey.forEach((b, i) => dv.setUint8(i, b));
    dv.setUint8(16, 84);
    let offset = 17;
    dv.setUint16(offset, 1); offset += 2;
    dv.setUint16(offset, 3); offset += 2;
    dv.setUint32(offset, 1); offset += 4;
    dv.setBigUint64(offset, 0n); offset += 8;
    dv.setBigUint64(offset, 0n); offset += 8;
    dv.setBigUint64(offset, 0n); offset += 8;
    dv.setBigUint64(offset, 0n); offset += 8;
    dv.setBigUint64(offset, 0n); offset += 8;
    dv.setUint32(offset, 0); offset += 4;
    dv.setBigUint64(offset, 0n); offset += 8;
    dv.setUint32(offset, 0); offset += 4;
    const opUL = [0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0D,0x01,0x01,0x01,0x01,0x01,0x01,0x00];
    opUL.forEach(b => { dv.setUint8(offset, b); offset++; });
    dv.setUint32(offset, 0);
    fs.writeFileSync(outputPath, new Uint8Array(buffer));
    console.log(`Dummy MXF written to ${outputPath}`);
  }
}

// Example usage:
// const parser = new MXFParser('path/to/file.mxf');
// parser.readAndPrint();
// parser.writeDummy();
  1. C Class (C++) for .MXF Handling

This C++ class opens an .MXF file, decodes/reads/prints properties (using big-endian macros), and writes a dummy file.

#include <iostream>
#include <fstream>
#include <vector>
#include <iomanip>
#include <cstdint>
#include <cstring>

class MXFParser {
private:
    std::string filepath;
    std::map<std::string, std::string> properties; // Use string for simplicity in printing

public:
    MXFParser(const std::string& fp) : filepath(fp) {}

    void readAndPrint() {
        std::ifstream file(filepath, std::ios::binary);
        if (!file) {
            std::cout << "Failed to open file." << std::endl;
            return;
        }
        file.seekg(0, std::ios::end);
        size_t size = file.tellg();
        file.seekg(0);
        std::vector<uint8_t> data(size);
        file.read(reinterpret_cast<char*>(data.data()), size);
        // Find Header Key
        uint8_t headerKey[14] = {0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x01}; // Partial
        size_t offset = 0;
        bool found = false;
        for (; offset < size - 16; ++offset) {
            if (std::memcmp(&data[offset], headerKey, 14) == 0) {
                found = true;
                break;
            }
        }
        if (!found) {
            std::cout << "Invalid MXF: Header Partition Pack not found." << std::endl;
            return;
        }
        offset += 16;
        // BER Length (simple)
        uint8_t firstLen = data[offset++];
        uint64_t length = 0;
        if (firstLen > 0x80) {
            int lenBytes = firstLen & 0x7F;
            for (int i = 0; i < lenBytes; ++i) {
                length = (length << 8) | data[offset++];
            }
        } else {
            length = firstLen;
        }
        // Parse Value (big-endian macros)
#define READ_U16() ((data[offset] << 8) | data[offset+1]); offset += 2;
#define READ_U32() ((data[offset] << 24) | (data[offset+1] << 16) | (data[offset+2] << 8) | data[offset+3]); offset += 4;
#define READ_U64() ((uint64_t)data[offset] << 56) | ((uint64_t)data[offset+1] << 48) | ((uint64_t)data[offset+2] << 40) | ((uint64_t)data[offset+3] << 32) | \
                   ((uint64_t)data[offset+4] << 24) | ((uint64_t)data[offset+5] << 16) | ((uint64_t)data[offset+6] << 8) | (uint64_t)data[offset+7]; offset += 8;
        properties["Major Version"] = std::to_string(READ_U16());
        properties["Minor Version"] = std::to_string(READ_U16());
        properties["KAG Size"] = std::to_string(READ_U32());
        properties["This Partition Offset"] = std::to_string(READ_U64());
        properties["Previous Partition Offset"] = std::to_string(READ_U64());
        properties["Footer Partition Offset"] = std::to_string(READ_U64());
        properties["Header Byte Count"] = std::to_string(READ_U64());
        properties["Index Byte Count"] = std::to_string(READ_U64());
        properties["Index SID"] = std::to_string(READ_U32());
        properties["Body Offset"] = std::to_string(READ_U64());
        properties["Body SID"] = std::to_string(READ_U32());
        // OP UL
        std::stringstream opSs;
        for (int i = 0; i < 16; ++i) opSs << std::hex << std::setw(2) << std::setfill('0') << std::uppercase << (int)data[offset++] << " ";
        properties["Operational Pattern"] = opSs.str().substr(0, opSs.str().size() - 1);
        // Essence Containers
        uint32_t ecCount = READ_U32();
        std::string ecs;
        for (uint32_t i = 0; i < ecCount; ++i) {
            std::stringstream ulSs;
            for (int j = 0; j < 16; ++j) ulSs << std::hex << std::setw(2) << std::setfill('0') << std::uppercase << (int)data[offset++] << " ";
            ecs += "[" + ulSs.str().substr(0, ulSs.str().size() - 1) + "] ";
        }
        properties["Essence Containers"] = ecs.empty() ? "[]" : ecs.substr(0, ecs.size() - 1);
#undef READ_U16
#undef READ_U32
#undef READ_U64
        // Print
        for (const auto& p : properties) {
            std::cout << p.first << ": " << p.second << std::endl;
        }
    }

    void writeDummy(const std::string& outputPath = "dummy.mxf") {
        std::vector<uint8_t> buffer(16 + 1 + 84);
        uint8_t key[16] = {0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x01,0x00,0x00};
        std::memcpy(buffer.data(), key, 16);
        buffer[16] = 84; // Len
        size_t offset = 17;
#define WRITE_U16(val) buffer[offset++] = (val >> 8) & 0xFF; buffer[offset++] = val & 0xFF;
#define WRITE_U32(val) buffer[offset++] = (val >> 24) & 0xFF; buffer[offset++] = (val >> 16) & 0xFF; buffer[offset++] = (val >> 8) & 0xFF; buffer[offset++] = val & 0xFF;
#define WRITE_U64(val) buffer[offset++] = (val >> 56) & 0xFF; buffer[offset++] = (val >> 48) & 0xFF; buffer[offset++] = (val >> 40) & 0xFF; buffer[offset++] = (val >> 32) & 0xFF; \
                       buffer[offset++] = (val >> 24) & 0xFF; buffer[offset++] = (val >> 16) & 0xFF; buffer[offset++] = (val >> 8) & 0xFF; buffer[offset++] = val & 0xFF;
        WRITE_U16(1); // Major
        WRITE_U16(3); // Minor
        WRITE_U32(1); // KAG
        WRITE_U64(0); // This
        WRITE_U64(0); // Prev
        WRITE_U64(0); // Footer
        WRITE_U64(0); // HeaderBC
        WRITE_U64(0); // IndexBC
        WRITE_U32(0); // IndexSID
        WRITE_U64(0); // BodyOffset
        WRITE_U32(0); // BodySID
        uint8_t opUL[16] = {0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0D,0x01,0x01,0x01,0x01,0x01,0x01,0x00};
        std::memcpy(&buffer[offset], opUL, 16); offset += 16;
        WRITE_U32(0); // EC count
#undef WRITE_U16
#undef WRITE_U32
#undef WRITE_U64
        std::ofstream out(outputPath, std::ios::binary);
        out.write(reinterpret_cast<const char*>(buffer.data()), buffer.size());
        std::cout << "Dummy MXF written to " << outputPath << std::endl;
    }
};

// Example usage:
// int main() {
//     MXFParser parser("path/to/file.mxf");
//     parser.readAndPrint();
//     parser.writeDummy();
//     return 0;
// }