Task 436: .MX File Format
Task 436: .MX File Format
File Format Specifications for the .MX File Format
The .MX file format appears to refer to the Material Exchange Format (MXF), a standardized container format for professional media content (video, audio, and metadata), defined by SMPTE ST 377-1:2019. (Note: The standard extension is .mxf, but the query uses .MX, which may be a shorthand or variant notation; no separate .mx format matches exactly, but MXF fits the description of a file format with detailed specifications and properties intrinsic to its structure. If this is not the intended format, please clarify.) MXF is designed for interchange of audio-visual material, using KLV (Key-Length-Value) encoding. It supports embedding or referencing essence (media streams) and metadata, with partitions for logical separation, multiplexing, and random access. The format is extensible via operational patterns (OPs) and plug-ins, with Big Endian byte order and optional alignment grid.
- List of all the properties of this file format intrinsic to its file system (structure and characteristics):
- Closed MXF File: File with Closed Header/Footer Partition containing Header Metadata; all values finalized/correct.
- Open MXF File: No Closed Header/Footer with Metadata; values unfinalized; decoders may decode but not required to support.
- Run-In Sequence: Optional non-KLV padding at start for alignment/synchronization (<65KB); decoders ignore and search for first Partition Pack Key.
- KLV Coding Sequence: Required KLV triplets (Key: 16-byte UL, Length: BER UInt64, Value); all data KLV-coded except Run-In.
- KLV Fill Items: Optional padding (Key: 06.0E.2B.34.01.01.01.02.03.01.02.10.01.00.00.00, Value: zero bytes, min 17 bytes).
- KLV Lengths: BER UInt64 (short/long-form up to 9 bytes); specific for Local Sets, Variable Packs, Defined Packs.
- MXF Keys and Universal Labels (ULs): 16-byte ULs registered in SMPTE ST 335/395/400; no recursive groupings unless specified.
- KLV Alignment Grid (KAG): Optional UInt32 byte alignment (1=no grid, 2-1048576=size); constant per Essence Container.
- MXF Byte Order: Big Endian for multi-byte values; essence per Container spec.
- Minimum MXF Decoder Requirements: Parse KLV, handle Partitions/references; locate Header Pack, read OP/Essence ULs, check compatibility.
- Strong Reference Integrity: UUID-based, one-to-one, same-file to Instance UID.
- Weak Reference Integrity: UL/UUID/UMID/PackageID, many-to-one, typed; unresolved OK for external/Dark.
- File Version Manipulation: Update VersionType; Major=1, Minor=3 for 2019 compliance.
- Partition Rules: Essence by BodySID; Index by IndexSID; unique SIDs; no Essence in Footer.
- Partition Status: Enum (Open/Incomplete, Closed/Incomplete, Open/Complete, Closed/Complete).
- Header Partition: Required first; BodySID=0, IndexSID=0; HeaderByteCount>0.
- Body Partition: Optional; BodySID>0; optional Metadata/Index/Essence.
- Footer Partition: Optional last; BodySID=0, IndexSID=0; always Closed.
- Header Metadata Repetition: Optional for recovery; track via Generation UID.
- Generation UID Tracking: UUID in Metadata Sets; update on modifications.
- Partition Pack Properties: MajorVer=0001h, MinorVer=0003h; KAGSize, This/Prev/NextPartition, Header/IndexByteCount, BodyOffset, OP UL, Essence Containers Batch.
- KLV Coded Dark Components: Mark unknown Essence/Metadata as "Dark".
- Local Set Lengths: 1/2/4-byte tags for Metadata.
- Variable/Defined-Length Packs: Dynamic/fixed sizes.
- Primer Pack: Required if Metadata; maps Local Tags to AUIDs (UInt16 Tag + 16-byte AUID).
- Encoding Constraints: No deviation from "shall" rules; UL/UUID storage with byte swap for AUID/IDAU.
- Generic UL: Common for all OPs.
- Generalized OP: Flexible item/package complexity.
- Specialized OP: Constrained subsets; may omit Footer.
- Package Hierarchy: Material Package refs Top-Level File Packages; Lower-Level for history.
- Preface Set (Root): LastModifiedDate (Timestamp), Version (VersionType=259/v1.3), ObjectModelVersion (UInt32=1), PrimaryPackage (WeakRef), Identifications (Array StrongRef), ContentStorage (StrongRef), OperationalPatternUL (UL), EssenceContainers (Batch UL), DMSchemes (Batch UL), ApplicationSchemesBatch (Batch UL), IsRIPPresent (Boolean).
- Identification Set: ThisGenerationUID (UUID), CompanyName (UTF-16), ProductName (UTF-16), ProductVersion (10-byte), VersionString (UTF-16), ProductUID (UUID), ModificationDate (Timestamp), ToolkitVersion (10-byte), Platform (UTF-16).
- Content Storage Set: Packages (Array StrongRef Package), EssenceData (Array StrongRef EssenceData).
- Two direct download links for files of format .MX (.mxf):
- Ghost blog embedded HTML JavaScript for drag and drop .MX file to dump properties:
- Python class for opening, decoding, reading, writing, and printing .MX properties:
import struct
import uuid
class MXFHandler:
def __init__(self, filepath=None):
self.filepath = filepath
self.properties = {}
if filepath:
self.read(filepath)
def read(self, filepath):
with open(filepath, 'rb') as f:
data = f.read()
self._parse_properties(data)
self.filepath = filepath
def _parse_properties(self, data):
# Parse Partition Pack Key (16 bytes)
key = '.'.join(f'{b:02x}' for b in data[0:16])
self.properties['Partition Pack Key'] = key
# Major/Minor Version (UInt16 BE each)
major, minor = struct.unpack('>BB', data[16:18])
self.properties['Major Version'] = major
self.properties['Minor Version'] = minor
# KAG Size (UInt32 BE)
self.properties['KAG Size'] = struct.unpack('>I', data[18:22])[0]
# This Partition (UInt64 BE)
self.properties['This Partition'] = struct.unpack('>Q', data[22:30])[0]
# Previous Partition (UInt64 BE)
self.properties['Previous Partition'] = struct.unpack('>Q', data[30:38])[0]
# Footer Partition (UInt64 BE)
self.properties['Footer Partition'] = struct.unpack('>Q', data[38:46])[0]
# Header Byte Count (UInt64 BE)
self.properties['Header Byte Count'] = struct.unpack('>Q', data[46:54])[0]
# Index Byte Count (UInt64 BE)
self.properties['Index Byte Count'] = struct.unpack('>Q', data[54:62])[0]
# Index SID (UInt32 BE)
self.properties['Index SID'] = struct.unpack('>I', data[62:66])[0]
# Body Offset (UInt64 BE)
self.properties['Body Offset'] = struct.unpack('>Q', data[66:74])[0]
# Body SID (UInt32 BE)
self.properties['Body SID'] = struct.unpack('>I', data[74:78])[0]
# Operational Pattern UL (16 bytes)
op_ul = '.'.join(f'{b:02x}' for b in data[78:94])
self.properties['Operational Pattern UL'] = op_ul
# Essence Containers Batch (UInt32 count, UInt32 len, then ULs)
essence_count = struct.unpack('>I', data[94:98])[0]
self.properties['Essence Containers Count'] = essence_count
# len = struct.unpack('>I', data[98:102])[0] # Typically 16 * count
self.properties['Essence Containers ULs'] = []
offset = 102
for _ in range(essence_count):
ul = '.'.join(f'{b:02x}' for b in data[offset:offset+16])
self.properties['Essence Containers ULs'].append(ul)
offset += 16
def print_properties(self):
for key, value in self.properties.items():
print(f"{key}: {value}")
def write(self, filepath=None):
if not filepath:
filepath = self.filepath or 'output.mxf'
# Basic write: reconstruct Partition Pack from properties (minimal, no full essence/metadata)
data = bytearray()
# Key (parse from string)
key_bytes = bytes(int(b, 16) for b in self.properties.get('Partition Pack Key', '06.0e.2b.34.02.05.01.01.0d.01.02.01.01.02.04.00').split('.'))
data.extend(key_bytes)
# Major/Minor
data.extend(struct.pack('>BB', self.properties.get('Major Version', 1), self.properties.get('Minor Version', 3)))
# KAG Size
data.extend(struct.pack('>I', self.properties.get('KAG Size', 1)))
# This/Previous/Footer Partition
data.extend(struct.pack('>Q', self.properties.get('This Partition', 0)))
data.extend(struct.pack('>Q', self.properties.get('Previous Partition', 0)))
data.extend(struct.pack('>Q', self.properties.get('Footer Partition', 0)))
# Header/Index Byte Count
data.extend(struct.pack('>Q', self.properties.get('Header Byte Count', 0)))
data.extend(struct.pack('>Q', self.properties.get('Index Byte Count', 0)))
# Index SID
data.extend(struct.pack('>I', self.properties.get('Index SID', 0)))
# Body Offset
data.extend(struct.pack('>Q', self.properties.get('Body Offset', 0)))
# Body SID
data.extend(struct.pack('>I', self.properties.get('Body SID', 0)))
# OP UL
op_bytes = bytes(int(b, 16) for b in self.properties.get('Operational Pattern UL', '06.0e.2b.34.04.01.01.0d.0d.01.02.01.01.02.00.00').split('.'))
data.extend(op_bytes)
# Essence Batch (count, len=16*count, ULs)
essence_uls = self.properties.get('Essence Containers ULs', [])
count = len(essence_uls)
data.extend(struct.pack('>I', count))
data.extend(struct.pack('>I', 16 * count))
for ul in essence_uls:
ul_bytes = bytes(int(b, 16) for b in ul.split('.'))
data.extend(ul_bytes)
with open(filepath, 'wb') as f:
f.write(data)
print(f"Written minimal MXF to {filepath}")
# Example usage:
# handler = MXFHandler('path/to/file.mxf')
# handler.print_properties()
# handler.write('new.mxf')
- Java class for opening, decoding, reading, writing, and printing .MX properties:
import java.io.*;
import java.nio.*;
import java.nio.file.*;
import java.util.*;
public class MXFHandler {
private String filepath;
private Map<String, Object> properties = new HashMap<>();
public MXFHandler(String filepath) {
this.filepath = filepath;
read(filepath);
}
public void read(String filepath) {
try {
byte[] data = Files.readAllBytes(Paths.get(filepath));
ByteBuffer buffer = ByteBuffer.wrap(data).order(ByteOrder.BIG_ENDIAN);
parseProperties(buffer);
} catch (IOException e) {
e.printStackTrace();
}
}
private void parseProperties(ByteBuffer buffer) {
// Partition Pack Key (16 bytes)
byte[] keyBytes = new byte[16];
buffer.get(keyBytes);
properties.put("Partition Pack Key", bytesToHex(keyBytes));
// Major/Minor Version
properties.put("Major Version", buffer.get() & 0xFF);
properties.put("Minor Version", buffer.get() & 0xFF);
// KAG Size (UInt32)
properties.put("KAG Size", buffer.getInt());
// This Partition (UInt64)
properties.put("This Partition", buffer.getLong());
// Previous Partition
properties.put("Previous Partition", buffer.getLong());
// Footer Partition
properties.put("Footer Partition", buffer.getLong());
// Header Byte Count
properties.put("Header Byte Count", buffer.getLong());
// Index Byte Count
properties.put("Index Byte Count", buffer.getLong());
// Index SID (UInt32)
properties.put("Index SID", buffer.getInt());
// Body Offset
properties.put("Body Offset", buffer.getLong());
// Body SID
properties.put("Body SID", buffer.getInt());
// Operational Pattern UL (16 bytes)
byte[] opBytes = new byte[16];
buffer.get(opBytes);
properties.put("Operational Pattern UL", bytesToHex(opBytes));
// Essence Containers Batch
int essenceCount = buffer.getInt();
properties.put("Essence Containers Count", essenceCount);
buffer.getInt(); // len, skip
List<String> uls = new ArrayList<>();
for (int i = 0; i < essenceCount; i++) {
byte[] ulBytes = new byte[16];
buffer.get(ulBytes);
uls.add(bytesToHex(ulBytes));
}
properties.put("Essence Containers ULs", uls);
}
private String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
if (sb.length() > 0) sb.append('.');
sb.append(String.format("%02x", b));
}
return sb.toString();
}
public void printProperties() {
properties.forEach((k, v) -> System.out.println(k + ": " + v));
}
public void write(String filepath) {
try (FileOutputStream fos = new FileOutputStream(filepath)) {
ByteBuffer buffer = ByteBuffer.allocate(1024).order(ByteOrder.BIG_ENDIAN);
// Key
hexToBytes((String) properties.getOrDefault("Partition Pack Key", "06.0e.2b.34.02.05.01.01.0d.01.02.01.01.02.04.00"), buffer);
// Major/Minor
buffer.put((byte) (int) properties.getOrDefault("Major Version", 1));
buffer.put((byte) (int) properties.getOrDefault("Minor Version", 3));
// KAG Size
buffer.putInt((int) properties.getOrDefault("KAG Size", 1));
// This/Previous/Footer
buffer.putLong((long) properties.getOrDefault("This Partition", 0L));
buffer.putLong((long) properties.getOrDefault("Previous Partition", 0L));
buffer.putLong((long) properties.getOrDefault("Footer Partition", 0L));
// Header/Index Byte Count
buffer.putLong((long) properties.getOrDefault("Header Byte Count", 0L));
buffer.putLong((long) properties.getOrDefault("Index Byte Count", 0L));
// Index SID
buffer.putInt((int) properties.getOrDefault("Index SID", 0));
// Body Offset
buffer.putLong((long) properties.getOrDefault("Body Offset", 0L));
// Body SID
buffer.putInt((int) properties.getOrDefault("Body SID", 0));
// OP UL
hexToBytes((String) properties.getOrDefault("Operational Pattern UL", "06.0e.2b.34.04.01.01.0d.0d.01.02.01.01.02.00.00"), buffer);
// Essence Batch
List<String> uls = (List<String>) properties.getOrDefault("Essence Containers ULs", new ArrayList<>());
int count = uls.size();
buffer.putInt(count);
buffer.putInt(16 * count);
for (String ul : uls) {
hexToBytes(ul, buffer);
}
buffer.flip();
fos.write(buffer.array(), 0, buffer.limit());
System.out.println("Written minimal MXF to " + filepath);
} catch (IOException e) {
e.printStackTrace();
}
}
private void hexToBytes(String hex, ByteBuffer buffer) {
String[] hexParts = hex.split("\\.");
for (String part : hexParts) {
buffer.put((byte) Integer.parseInt(part, 16));
}
}
// Example usage:
// public static void main(String[] args) {
// MXFHandler handler = new MXFHandler("path/to/file.mxf");
// handler.printProperties();
// handler.write("new.mxf");
// }
}
- JavaScript class for opening, decoding, reading, writing, and printing .MX properties:
class MXFHandler {
constructor(filepath = null) {
this.filepath = filepath;
this.properties = {};
if (filepath) {
this.read(filepath);
}
}
async read(filepath) {
try {
const response = await fetch(filepath);
const arrayBuffer = await response.arrayBuffer();
const dataView = new DataView(arrayBuffer);
this.parseProperties(dataView);
} catch (e) {
console.error(e);
}
}
parseProperties(dataView) {
// Partition Pack Key (16 bytes)
let key = [];
for (let i = 0; i < 16; i++) {
key.push(dataView.getUint8(i).toString(16).padStart(2, '0'));
}
this.properties['Partition Pack Key'] = key.join('.');
// Major/Minor Version
this.properties['Major Version'] = dataView.getUint8(16);
this.properties['Minor Version'] = dataView.getUint8(17);
// KAG Size
this.properties['KAG Size'] = dataView.getUint32(18);
// This Partition
this.properties['This Partition'] = dataView.getBigUint64(22);
// Previous Partition
this.properties['Previous Partition'] = dataView.getBigUint64(30);
// Footer Partition
this.properties['Footer Partition'] = dataView.getBigUint64(38);
// Header Byte Count
this.properties['Header Byte Count'] = dataView.getBigUint64(46);
// Index Byte Count
this.properties['Index Byte Count'] = dataView.getBigUint64(54);
// Index SID
this.properties['Index SID'] = dataView.getUint32(62);
// Body Offset
this.properties['Body Offset'] = dataView.getBigUint64(66);
// Body SID
this.properties['Body SID'] = dataView.getUint32(74);
// Operational Pattern UL
let op = [];
for (let i = 78; i < 94; i++) {
op.push(dataView.getUint8(i).toString(16).padStart(2, '0'));
}
this.properties['Operational Pattern UL'] = op.join('.');
// Essence Containers Batch
const essenceCount = dataView.getUint32(94);
this.properties['Essence Containers Count'] = essenceCount;
// Skip len at 98
this.properties['Essence Containers ULs'] = [];
let offset = 102;
for (let i = 0; i < essenceCount; i++) {
let ul = [];
for (let j = 0; j < 16; j++) {
ul.push(dataView.getUint8(offset + j).toString(16).padStart(2, '0'));
}
this.properties['Essence Containers ULs'].push(ul.join('.'));
offset += 16;
}
}
printProperties() {
console.log(this.properties);
}
write(filepath) {
// Basic write using Blob (for browser download)
const buffer = new ArrayBuffer(1024);
const dataView = new DataView(buffer);
// Key
const keyParts = this.properties['Partition Pack Key'] ? this.properties['Partition Pack Key'].split('.') : '06.0e.2b.34.02.05.01.01.0d.01.02.01.01.02.04.00'.split('.');
keyParts.forEach((part, i) => dataView.setUint8(i, parseInt(part, 16)));
let offset = 16;
// Major/Minor
dataView.setUint8(offset++, this.properties['Major Version'] || 1);
dataView.setUint8(offset++, this.properties['Minor Version'] || 3);
// KAG Size
dataView.setUint32(offset, this.properties['KAG Size'] || 1);
offset += 4;
// This/Previous/Footer
dataView.setBigUint64(offset, BigInt(this.properties['This Partition'] || 0));
offset += 8;
dataView.setBigUint64(offset, BigInt(this.properties['Previous Partition'] || 0));
offset += 8;
dataView.setBigUint64(offset, BigInt(this.properties['Footer Partition'] || 0));
offset += 8;
// Header/Index Byte Count
dataView.setBigUint64(offset, BigInt(this.properties['Header Byte Count'] || 0));
offset += 8;
dataView.setBigUint64(offset, BigInt(this.properties['Index Byte Count'] || 0));
offset += 8;
// Index SID
dataView.setUint32(offset, this.properties['Index SID'] || 0);
offset += 4;
// Body Offset
dataView.setBigUint64(offset, BigInt(this.properties['Body Offset'] || 0));
offset += 8;
// Body SID
dataView.setUint32(offset, this.properties['Body SID'] || 0);
offset += 4;
// OP UL
const opParts = this.properties['Operational Pattern UL'] ? this.properties['Operational Pattern UL'].split('.') : '06.0e.2b.34.04.01.01.0d.0d.01.02.01.01.02.00.00'.split('.');
opParts.forEach((part) => dataView.setUint8(offset++, parseInt(part, 16)));
// Essence Batch
const uls = this.properties['Essence Containers ULs'] || [];
dataView.setUint32(offset, uls.length);
offset += 4;
dataView.setUint32(offset, 16 * uls.length);
offset += 4;
uls.forEach((ul) => {
ul.split('.').forEach((part) => dataView.setUint8(offset++, parseInt(part, 16)));
});
const blob = new Blob([buffer.slice(0, offset)], {type: 'application/octet-stream'});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filepath || 'output.mxf';
a.click();
console.log('Written minimal MXF as download.');
}
}
// Example usage:
// const handler = new MXFHandler('path/to/file.mxf');
// handler.printProperties();
// handler.write('new.mxf');
- C class (using C++ for class support) for opening, decoding, reading, writing, and printing .MX properties:
#include <iostream>
#include <fstream>
#include <iomanip>
#include <vector>
#include <cstdint>
#include <cstring>
#include <endian.h> // For big endian conversions, may need to include <byteswap.h> on some systems
class MXFHandler {
private:
std::string filepath;
std::map<std::string, std::string> properties; // Use string for simplicity, convert as needed
public:
MXFHandler(const std::string& fp = "") : filepath(fp) {
if (!fp.empty()) read(fp);
}
void read(const std::string& fp) {
filepath = fp;
std::ifstream file(fp, std::ios::binary);
if (!file) {
std::cerr << "Error opening 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);
parseProperties(data.data());
}
void parseProperties(const uint8_t* data) {
// Partition Pack Key (16 bytes)
std::stringstream ss;
for (int i = 0; i < 16; ++i) {
if (i > 0) ss << '.';
ss << std::setfill('0') << std::setw(2) << std::hex << static_cast<int>(data[i]);
}
properties["Partition Pack Key"] = ss.str();
// Major/Minor Version
properties["Major Version"] = std::to_string(data[16]);
properties["Minor Version"] = std::to_string(data[17]);
// KAG Size (be32toh)
uint32_t kag = be32toh(*reinterpret_cast<const uint32_t*>(data + 18));
properties["KAG Size"] = std::to_string(kag);
// This Partition (be64toh)
uint64_t thisPart = be64toh(*reinterpret_cast<const uint64_t*>(data + 22));
properties["This Partition"] = std::to_string(thisPart);
// Previous Partition
uint64_t prevPart = be64toh(*reinterpret_cast<const uint64_t*>(data + 30));
properties["Previous Partition"] = std::to_string(prevPart);
// Footer Partition
uint64_t footPart = be64toh(*reinterpret_cast<const uint64_t*>(data + 38));
properties["Footer Partition"] = std::to_string(footPart);
// Header Byte Count
uint64_t headCount = be64toh(*reinterpret_cast<const uint64_t*>(data + 46));
properties["Header Byte Count"] = std::to_string(headCount);
// Index Byte Count
uint64_t indexCount = be64toh(*reinterpret_cast<const uint64_t*>(data + 54));
properties["Index Byte Count"] = std::to_string(indexCount);
// Index SID
uint32_t indexSid = be32toh(*reinterpret_cast<const uint32_t*>(data + 62));
properties["Index SID"] = std::to_string(indexSid);
// Body Offset
uint64_t bodyOff = be64toh(*reinterpret_cast<const uint64_t*>(data + 66));
properties["Body Offset"] = std::to_string(bodyOff);
// Body SID
uint32_t bodySid = be32toh(*reinterpret_cast<const uint32_t*>(data + 74));
properties["Body SID"] = std::to_string(bodySid);
// Operational Pattern UL (16 bytes)
ss.str("");
for (int i = 78; i < 94; ++i) {
if (i > 78) ss << '.';
ss << std::setfill('0') << std::setw(2) << std::hex << static_cast<int>(data[i]);
}
properties["Operational Pattern UL"] = ss.str();
// Essence Containers Batch
uint32_t essenceCount = be32toh(*reinterpret_cast<const uint32_t*>(data + 94));
properties["Essence Containers Count"] = std::to_string(essenceCount);
// Skip len at 98
std::string ulsStr;
size_t offset = 102;
for (uint32_t i = 0; i < essenceCount; ++i) {
if (i > 0) ulsStr += ", ";
ss.str("");
for (int j = 0; j < 16; ++j) {
if (j > 0) ss << '.';
ss << std::setfill('0') << std::setw(2) << std::hex << static_cast<int>(data[offset + j]);
}
ulsStr += ss.str();
offset += 16;
}
properties["Essence Containers ULs"] = ulsStr;
}
void printProperties() {
for (const auto& prop : properties) {
std::cout << prop.first << ": " << prop.second << std::endl;
}
}
void write(const std::string& fp) {
std::ofstream file(fp, std::ios::binary);
if (!file) {
std::cerr << "Error writing file" << std::endl;
return;
}
std::vector<uint8_t> data(1024, 0);
// Key
std::string keyStr = properties["Partition Pack Key"].empty() ? "06.0e.2b.34.02.05.01.01.0d.01.02.01.01.02.04.00" : properties["Partition Pack Key"];
auto parts = split(keyStr, '.');
for (size_t i = 0; i < 16; ++i) {
data[i] = static_cast<uint8_t>(std::stoi(parts[i], nullptr, 16));
}
size_t offset = 16;
// Major/Minor
data[offset++] = static_cast<uint8_t>(std::stoi(properties["Major Version"].empty() ? "1" : properties["Major Version"]));
data[offset++] = static_cast<uint8_t>(std::stoi(properties["Minor Version"].empty() ? "3" : properties["Minor Version"]));
// KAG Size
uint32_t kag = htobe32(std::stoi(properties["KAG Size"].empty() ? "1" : properties["KAG Size"]));
std::memcpy(data.data() + offset, &kag, 4);
offset += 4;
// This/Previous/Footer
uint64_t thisPart = htobe64(std::stoull(properties["This Partition"].empty() ? "0" : properties["This Partition"]));
std::memcpy(data.data() + offset, &thisPart, 8);
offset += 8;
uint64_t prevPart = htobe64(std::stoull(properties["Previous Partition"].empty() ? "0" : properties["Previous Partition"]));
std::memcpy(data.data() + offset, &prevPart, 8);
offset += 8;
uint64_t footPart = htobe64(std::stoull(properties["Footer Partition"].empty() ? "0" : properties["Footer Partition"]));
std::memcpy(data.data() + offset, &footPart, 8);
offset += 8;
// Header/Index Byte Count
uint64_t headCount = htobe64(std::stoull(properties["Header Byte Count"].empty() ? "0" : properties["Header Byte Count"]));
std::memcpy(data.data() + offset, &headCount, 8);
offset += 8;
uint64_t indexCount = htobe64(std::stoull(properties["Index Byte Count"].empty() ? "0" : properties["Index Byte Count"]));
std::memcpy(data.data() + offset, &indexCount, 8);
offset += 8;
// Index SID
uint32_t indexSid = htobe32(std::stoi(properties["Index SID"].empty() ? "0" : properties["Index SID"]));
std::memcpy(data.data() + offset, &indexSid, 4);
offset += 4;
// Body Offset
uint64_t bodyOff = htobe64(std::stoull(properties["Body Offset"].empty() ? "0" : properties["Body Offset"]));
std::memcpy(data.data() + offset, &bodyOff, 8);
offset += 8;
// Body SID
uint32_t bodySid = htobe32(std::stoi(properties["Body SID"].empty() ? "0" : properties["Body SID"]));
std::memcpy(data.data() + offset, &bodySid, 4);
offset += 4;
// OP UL
std::string opStr = properties["Operational Pattern UL"].empty() ? "06.0e.2b.34.04.01.01.0d.0d.01.02.01.01.02.00.00" : properties["Operational Pattern UL"];
parts = split(opStr, '.');
for (size_t i = 0; i < 16; ++i) {
data[offset++] = static_cast<uint8_t>(std::stoi(parts[i], nullptr, 16));
}
// Essence Batch
std::string ulsStr = properties["Essence Containers ULs"];
auto ulList = split(ulsStr, ',');
uint32_t count = ulList.size();
uint32_t countBe = htobe32(count);
std::memcpy(data.data() + offset, &countBe, 4);
offset += 4;
uint32_t lenBe = htobe32(16 * count);
std::memcpy(data.data() + offset, &lenBe, 4);
offset += 4;
for (const auto& ul : ulList) {
auto ulParts = split(ul, '.');
for (size_t i = 0; i < 16; ++i) {
data[offset++] = static_cast<uint8_t>(std::stoi(ulParts[i], nullptr, 16));
}
}
file.write(reinterpret_cast<const char*>(data.data()), offset);
std::cout << "Written minimal MXF to " << fp << std::endl;
}
private:
std::vector<std::string> split(const std::string& s, char delim) {
std::vector<std::string> result;
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim)) {
result.push_back(item);
}
return result;
}
};
// Example usage:
// int main() {
// MXFHandler handler("path/to/file.mxf");
// handler.printProperties();
// handler.write("new.mxf");
// return 0;
// }