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).
- 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.
- Two Direct Download Links for .MXF Files
- https://filesamples.com/samples/video/mxf/sample_1280x720_surfing_with_audio.mxf (1280x720 resolution with audio, ~10 MB).
- https://filesamples.com/samples/video/mxf/sample_960x400_ocean_with_audio.mxf (960x400 resolution with audio, ~5 MB).
- 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).
- 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()
- 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");
// }
}
- 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();
- 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;
// }