Task 367: .M2TS File Format
Task 367: .M2TS File Format
File Format Specifications for .M2TS
The .M2TS file format, also known as BDAV MPEG-2 Transport Stream, is a container format for multiplexing high-definition audio, video, and other streams (such as subtitles). It is primarily used for Blu-ray Discs (BD-ROM) and AVCHD camcorders. The format is a modification of the standard MPEG-2 Transport Stream (ITU-T H.222.0 | ISO/IEC 13818-1) to support random access on storage media like discs, hard drives, or memory cards. Unlike standard MPEG-2 TS, which can use constant bit rates, .M2TS employs variable bit rates to optimize storage. The maximum multiplex rate is 48 Mbps.
Key modifications include prefixing each 188-byte MPEG-2 TS packet with a 4-byte TP_extra_header, resulting in 192-byte source packets. These source packets are grouped into aligned units of 6144 bytes (32 packets) for efficient disc sector alignment. Files are typically named zzzzz.m2ts (where zzzzz is a 5-digit clip number), and associated metadata is stored in separate .clpi files. There is no traditional magic number, but validation can involve checking file size modulo 192 == 0, the TS sync byte (0x47) at offset 4 of each packet, and consistent structure.
Supported video codecs include H.262/MPEG-2 Part 2, H.264/MPEG-4 AVC, and SMPTE VC-1. Supported audio codecs include Dolby Digital (AC-3), DTS, Linear PCM, Dolby Digital Plus, DTS-HD High Resolution Audio, DTS-HD Master Audio, and Dolby TrueHD (with restrictions in AVCHD to AC-3 or LPCM). MIME type is video/mp2t.
List of all the properties of this file format intrinsic to its file system:
- File extension: .m2ts (or .mts for AVCHD with 8.3 naming)
- MIME type: video/mp2t
- Base format: Modified MPEG-2 Transport Stream (ITU-T H.222.0 | ISO/IEC 13818-1)
- Source packet size: 192 bytes
- TP_extra_header size: 4 bytes
- TP_extra_header fields: Copy permission indicator (2 bits: 00 = no protection, 01 = copy once, 10 = no more copies, 11 = never copy), Arrival time stamp (30 bits, 27 MHz resolution)
- MPEG-2 TS packet size: 188 bytes (starts with sync byte 0x47)
- Aligned unit size: 6144 bytes (3 logical sectors of 2048 bytes each)
- Packets per aligned unit: 32
- Bit rate type: Variable (VBR)
- Maximum multiplex rate: 48 Mbps
- Supported video codecs: H.262/MPEG-2 Part 2, H.264/MPEG-4 AVC, SMPTE VC-1
- Supported audio codecs: Dolby Digital (AC-3), DTS, Linear PCM, Dolby Digital Plus, DTS-HD High Resolution Audio, DTS-HD Master Audio, Dolby TrueHD
- Multiplexing: Audio, video, subtitles, and other streams
- Random access support: Yes (via TP_extra_header timestamps and aligned units)
- Encryption support: None intrinsic (handled by container systems like AACS for Blu-ray)
- Validation traits: File size modulo 192 == 0, TS sync byte at offset 4 of each source packet
Two direct download links for .M2TS files:
- https://filesamples.com/samples/video/m2ts/sample_640x360.m2ts
- https://filesamples.com/samples/video/m2ts/sample_960x540.m2ts
Ghost blog embedded HTML JavaScript for drag-and-drop .M2TS file to dump properties:
Drag and Drop .M2TS File to Dump Properties
- Python class:
import struct
import os
class M2TSHandler:
def __init__(self, filepath):
self.filepath = filepath
self.properties = {
'File extension': '.m2ts',
'MIME type': 'video/mp2t',
'Base format': 'Modified MPEG-2 Transport Stream (ITU-T H.222.0 | ISO/IEC 13818-1)',
'Source packet size': '192 bytes',
'TP_extra_header size': '4 bytes',
'TP_extra_header fields': 'Copy permission indicator (2 bits), Arrival time stamp (30 bits)',
'MPEG-2 TS packet size': '188 bytes',
'Aligned unit size': '6144 bytes',
'Packets per aligned unit': '32',
'Bit rate type': 'Variable (VBR)',
'Maximum multiplex rate': '48 Mbps',
'Supported video codecs': 'H.262/MPEG-2 Part 2, H.264/MPEG-4 AVC, SMPTE VC-1',
'Supported audio codecs': 'Dolby Digital (AC-3), DTS, Linear PCM, Dolby Digital Plus, DTS-HD High Resolution Audio, DTS-HD Master Audio, Dolby TrueHD',
'Multiplexing': 'Audio, video, subtitles, and other streams',
'Random access support': 'Yes',
'Encryption support': 'None intrinsic',
'Validation traits': 'File size modulo 192 == 0, TS sync byte at offset 4'
}
def decode_read(self):
with open(self.filepath, 'rb') as f:
data = f.read()
file_size = len(data)
if file_size % 192 != 0 or data[4] != 0x47:
raise ValueError('Invalid .M2TS file')
num_packets = file_size // 192
# Decode first packet
tp_extra_header = struct.unpack('>I', data[0:4])[0]
copy_permission = (tp_extra_header >> 30) & 0x03
first_ats = tp_extra_header & 0x3FFFFFFF
# Decode last packet
last_offset = file_size - 192
last_tp_extra_header = struct.unpack('>I', data[last_offset:last_offset+4])[0]
last_ats = last_tp_extra_header & 0x3FFFFFFF
duration = (last_ats - first_ats) / 27000000.0
return {
'file_size': file_size,
'num_packets': num_packets,
'copy_permission': copy_permission,
'first_ats': first_ats,
'last_ats': last_ats,
'duration': duration
}
def print_properties(self):
instance_props = self.decode_read()
print('General Properties:')
for key, value in self.properties.items():
print(f'{key}: {value}')
print('\nInstance Properties:')
for key, value in instance_props.items():
print(f'{key}: {value}')
def write(self, output_path, modify_ats=False):
with open(self.filepath, 'rb') as f:
data = bytearray(f.read())
if modify_ats:
# Example: increment first ATS by 1 (demo write capability)
tp_extra_header = struct.unpack('>I', data[0:4])[0]
copy_permission = (tp_extra_header >> 30) & 0x03
ats = (tp_extra_header & 0x3FFFFFFF) + 1
new_header = (copy_permission << 30) | ats
struct.pack_into('>I', data, 0, new_header)
with open(output_path, 'wb') as f:
f.write(data)
print(f'Written to {output_path}')
# Example usage:
# handler = M2TSHandler('sample.m2ts')
# handler.print_properties()
# handler.write('modified.m2ts', modify_ats=True)
- Java class:
import java.io.*;
import java.nio.*;
import java.nio.file.*;
public class M2TSHandler {
private String filepath;
private java.util.Map<String, String> properties = new java.util.HashMap<>();
public M2TSHandler(String filepath) {
this.filepath = filepath;
properties.put("File extension", ".m2ts");
properties.put("MIME type", "video/mp2t");
properties.put("Base format", "Modified MPEG-2 Transport Stream (ITU-T H.222.0 | ISO/IEC 13818-1)");
properties.put("Source packet size", "192 bytes");
properties.put("TP_extra_header size", "4 bytes");
properties.put("TP_extra_header fields", "Copy permission indicator (2 bits), Arrival time stamp (30 bits)");
properties.put("MPEG-2 TS packet size", "188 bytes");
properties.put("Aligned unit size", "6144 bytes");
properties.put("Packets per aligned unit", "32");
properties.put("Bit rate type", "Variable (VBR)");
properties.put("Maximum multiplex rate", "48 Mbps");
properties.put("Supported video codecs", "H.262/MPEG-2 Part 2, H.264/MPEG-4 AVC, SMPTE VC-1");
properties.put("Supported audio codecs", "Dolby Digital (AC-3), DTS, Linear PCM, Dolby Digital Plus, DTS-HD High Resolution Audio, DTS-HD Master Audio, Dolby TrueHD");
properties.put("Multiplexing", "Audio, video, subtitles, and other streams");
properties.put("Random access support", "Yes");
properties.put("Encryption support", "None intrinsic");
properties.put("Validation traits", "File size modulo 192 == 0, TS sync byte at offset 4");
}
public java.util.Map<String, Object> decodeRead() throws IOException {
byte[] data = Files.readAllBytes(Paths.get(filepath));
long fileSize = data.length;
if (fileSize % 192 != 0 || (data[4] & 0xFF) != 0x47) {
throw new IllegalArgumentException("Invalid .M2TS file");
}
long numPackets = fileSize / 192;
// Decode first packet (big-endian)
ByteBuffer bb = ByteBuffer.wrap(data, 0, 4).order(ByteOrder.BIG_ENDIAN);
int tpExtraHeader = bb.getInt();
int copyPermission = (tpExtraHeader >>> 30) & 0x03;
long firstAts = tpExtraHeader & 0x3FFFFFFF;
// Decode last packet
bb = ByteBuffer.wrap(data, (int)(fileSize - 192), 4).order(ByteOrder.BIG_ENDIAN);
int lastTpExtraHeader = bb.getInt();
long lastAts = lastTpExtraHeader & 0x3FFFFFFF;
double duration = (lastAts - firstAts) / 27000000.0;
java.util.Map<String, Object> instanceProps = new java.util.HashMap<>();
instanceProps.put("file_size", fileSize);
instanceProps.put("num_packets", numPackets);
instanceProps.put("copy_permission", copyPermission);
instanceProps.put("first_ats", firstAts);
instanceProps.put("last_ats", lastAts);
instanceProps.put("duration", duration);
return instanceProps;
}
public void printProperties() throws IOException {
java.util.Map<String, Object> instanceProps = decodeRead();
System.out.println("General Properties:");
properties.forEach((key, value) -> System.out.println(key + ": " + value));
System.out.println("\nInstance Properties:");
instanceProps.forEach((key, value) -> System.out.println(key + ": " + value));
}
public void write(String outputPath, boolean modifyAts) throws IOException {
byte[] data = Files.readAllBytes(Paths.get(filepath));
if (modifyAts) {
// Example: increment first ATS by 1
ByteBuffer bb = ByteBuffer.wrap(data, 0, 4).order(ByteOrder.BIG_ENDIAN);
int tpExtraHeader = bb.getInt();
int copyPermission = (tpExtraHeader >>> 30) & 0x03;
long ats = (tpExtraHeader & 0x3FFFFFFF) + 1;
int newHeader = (copyPermission << 30) | (int)ats;
bb.rewind();
bb.putInt(newHeader);
}
Files.write(Paths.get(outputPath), data);
System.out.println("Written to " + outputPath);
}
// Example usage:
// public static void main(String[] args) throws IOException {
// M2TSHandler handler = new M2TSHandler("sample.m2ts");
// handler.printProperties();
// handler.write("modified.m2ts", true);
// }
}
- JavaScript class:
class M2TSHandler {
constructor(filepath) {
this.filepath = filepath; // Note: JS can't directly open local files; use with FileReader or Node.js fs
this.properties = {
'File extension': '.m2ts',
'MIME type': 'video/mp2t',
'Base format': 'Modified MPEG-2 Transport Stream (ITU-T H.222.0 | ISO/IEC 13818-1)',
'Source packet size': '192 bytes',
'TP_extra_header size': '4 bytes',
'TP_extra_header fields': 'Copy permission indicator (2 bits), Arrival time stamp (30 bits)',
'MPEG-2 TS packet size': '188 bytes',
'Aligned unit size': '6144 bytes',
'Packets per aligned unit': '32',
'Bit rate type': 'Variable (VBR)',
'Maximum multiplex rate': '48 Mbps',
'Supported video codecs': 'H.262/MPEG-2 Part 2, H.264/MPEG-4 AVC, SMPTE VC-1',
'Supported audio codecs': 'Dolby Digital (AC-3), DTS, Linear PCM, Dolby Digital Plus, DTS-HD High Resolution Audio, DTS-HD Master Audio, Dolby TrueHD',
'Multiplexing': 'Audio, video, subtitles, and other streams',
'Random access support': 'Yes',
'Encryption support': 'None intrinsic',
'Validation traits': 'File size modulo 192 == 0, TS sync byte at offset 4'
};
}
async decodeRead(buffer) { // Accepts ArrayBuffer (e.g., from FileReader or fs.readFileSync in Node)
const dataView = new DataView(buffer);
const fileSize = buffer.byteLength;
if (fileSize % 192 !== 0 || dataView.getUint8(4) !== 0x47) {
throw new Error('Invalid .M2TS file');
}
const numPackets = fileSize / 192;
const tpExtraHeader = dataView.getUint32(0, false); // Big-endian
const copyPermission = (tpExtraHeader >>> 30) & 0x03;
const firstAts = tpExtraHeader & 0x3FFFFFFF;
const lastOffset = fileSize - 192;
const lastTpExtraHeader = dataView.getUint32(lastOffset, false);
const lastAts = lastTpExtraHeader & 0x3FFFFFFF;
const duration = (lastAts - firstAts) / 27000000;
return {
file_size: fileSize,
num_packets: numPackets,
copy_permission: copyPermission,
first_ats: firstAts,
last_ats: lastAts,
duration: duration
};
}
async printProperties(buffer) {
const instanceProps = await this.decodeRead(buffer);
console.log('General Properties:');
for (const [key, value] of Object.entries(this.properties)) {
console.log(`${key}: ${value}`);
}
console.log('\nInstance Properties:');
for (const [key, value] of Object.entries(instanceProps)) {
console.log(`${key}: ${value}`);
}
}
async write(outputPath, modifyAts = false, buffer) { // Node.js only; use fs
let data = new Uint8Array(buffer);
if (modifyAts) {
const dataView = new DataView(data.buffer);
let tpExtraHeader = dataView.getUint32(0, false);
const copyPermission = (tpExtraHeader >>> 30) & 0x03;
let ats = (tpExtraHeader & 0x3FFFFFFF) + 1;
const newHeader = (copyPermission << 30) | ats;
dataView.setUint32(0, newHeader, false);
}
// Assuming Node.js fs
const fs = require('fs');
fs.writeFileSync(outputPath, data);
console.log(`Written to ${outputPath}`);
}
}
// Example usage in browser (with FileReader):
// const file = ... // from input or drop
// const reader = new FileReader();
// reader.onload = async (e) => {
// const handler = new M2TSHandler('sample.m2ts');
// await handler.printProperties(e.target.result);
// };
// reader.readAsArrayBuffer(file);
// In Node.js:
// const fs = require('fs');
// const buffer = fs.readFileSync('sample.m2ts');
// const handler = new M2TSHandler('sample.m2ts');
// handler.printProperties(buffer);
// handler.write('modified.m2ts', true, buffer);
- C class (using C++ for class support):
#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <cstdint>
#include <cstring>
class M2TSHandler {
private:
std::string filepath;
std::map<std::string, std::string> properties;
public:
M2TSHandler(const std::string& fp) : filepath(fp) {
properties["File extension"] = ".m2ts";
properties["MIME type"] = "video/mp2t";
properties["Base format"] = "Modified MPEG-2 Transport Stream (ITU-T H.222.0 | ISO/IEC 13818-1)";
properties["Source packet size"] = "192 bytes";
properties["TP_extra_header size"] = "4 bytes";
properties["TP_extra_header fields"] = "Copy permission indicator (2 bits), Arrival time stamp (30 bits)";
properties["MPEG-2 TS packet size"] = "188 bytes";
properties["Aligned unit size"] = "6144 bytes";
properties["Packets per aligned unit"] = "32";
properties["Bit rate type"] = "Variable (VBR)";
properties["Maximum multiplex rate"] = "48 Mbps";
properties["Supported video codecs"] = "H.262/MPEG-2 Part 2, H.264/MPEG-4 AVC, SMPTE VC-1";
properties["Supported audio codecs"] = "Dolby Digital (AC-3), DTS, Linear PCM, Dolby Digital Plus, DTS-HD High Resolution Audio, DTS-HD Master Audio, Dolby TrueHD";
properties["Multiplexing"] = "Audio, video, subtitles, and other streams";
properties["Random access support"] = "Yes";
properties["Encryption support"] = "None intrinsic";
properties["Validation traits"] = "File size modulo 192 == 0, TS sync byte at offset 4";
}
std::map<std::string, double> decodeRead() {
std::ifstream file(filepath, std::ios::binary | std::ios::ate);
if (!file) {
throw std::runtime_error("Cannot open file");
}
size_t fileSize = file.tellg();
file.seekg(0);
std::vector<uint8_t> data(fileSize);
file.read(reinterpret_cast<char*>(data.data()), fileSize);
if (fileSize % 192 != 0 || data[4] != 0x47) {
throw std::runtime_error("Invalid .M2TS file");
}
double numPackets = static_cast<double>(fileSize) / 192.0;
// Decode first packet (big-endian)
uint32_t tpExtraHeader = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
uint32_t copyPermission = (tpExtraHeader >> 30) & 0x03;
uint32_t firstAts = tpExtraHeader & 0x3FFFFFFF;
// Decode last packet
size_t lastOffset = fileSize - 192;
uint32_t lastTpExtraHeader = (data[lastOffset] << 24) | (data[lastOffset+1] << 16) | (data[lastOffset+2] << 8) | data[lastOffset+3];
uint32_t lastAts = lastTpExtraHeader & 0x3FFFFFFF;
double duration = static_cast<double>(lastAts - firstAts) / 27000000.0;
std::map<std::string, double> instanceProps;
instanceProps["file_size"] = fileSize;
instanceProps["num_packets"] = numPackets;
instanceProps["copy_permission"] = copyPermission;
instanceProps["first_ats"] = firstAts;
instanceProps["last_ats"] = lastAts;
instanceProps["duration"] = duration;
return instanceProps;
}
void printProperties() {
auto instanceProps = decodeRead();
std::cout << "General Properties:" << std::endl;
for (const auto& prop : properties) {
std::cout << prop.first << ": " << prop.second << std::endl;
}
std::cout << "\nInstance Properties:" << std::endl;
for (const auto& prop : instanceProps) {
std::cout << prop.first << ": " << prop.second << std::endl;
}
}
void write(const std::string& outputPath, bool modifyAts) {
std::ifstream inFile(filepath, std::ios::binary);
if (!inFile) {
throw std::runtime_error("Cannot open input file");
}
std::vector<uint8_t> data((std::istreambuf_iterator<char>(inFile)), std::istreambuf_iterator<char>());
if (modifyAts) {
// Example: increment first ATS by 1
uint32_t tpExtraHeader = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
uint32_t copyPermission = (tpExtraHeader >> 30) & 0x03;
uint32_t ats = (tpExtraHeader & 0x3FFFFFFF) + 1;
uint32_t newHeader = (copyPermission << 30) | ats;
data[0] = (newHeader >> 24) & 0xFF;
data[1] = (newHeader >> 16) & 0xFF;
data[2] = (newHeader >> 8) & 0xFF;
data[3] = newHeader & 0xFF;
}
std::ofstream outFile(outputPath, std::ios::binary);
outFile.write(reinterpret_cast<const char*>(data.data()), data.size());
std::cout << "Written to " << outputPath << std::endl;
}
};
// Example usage:
// int main() {
// try {
// M2TSHandler handler("sample.m2ts");
// handler.printProperties();
// handler.write("modified.m2ts", true);
// } catch (const std::exception& e) {
// std::cerr << e.what() << std::endl;
// }
// return 0;
// }