Task 373: .M4V File Format
Task 373: .M4V File Format
1. List of Properties Intrinsic to the .M4V File Format
Based on the specifications from the ISO Base Media File Format (ISO/IEC 14496-12), which .M4V extends as a variant of MP4 developed by Apple, the intrinsic properties of the .M4V file format include the following. These are derived from the file's structural elements, particularly the file type box (ftyp), which defines compatibility and identification, as well as general format attributes. Note that .M4V files follow a box-based structure (also called atoms), starting with an 8-byte header per box (4-byte big-endian size, 4-byte type), and the first box is always 'ftyp'. The properties focus on those that identify and define the format at the file level:
- Filename Extension: .m4v
- MIME Type: video/x-m4v
- Developer: Apple Inc.
- Container Basis: MPEG-4 Part 14 (extended from QuickTime File Format / ISO Base Media File Format)
- File Signature (Magic Number): Offset 4-7: 'ftyp' (hex: 66 74 79 70)
- Major Brand: Typically 'M4V ' (hex: 4D 34 56 20), indicating the Apple-specific M4V subtype
- Minor Version: Typically 0 (4-byte big-endian integer)
- Compatible Brands: A list of 4-byte ASCII strings (e.g., 'M4V ', 'mp42', 'isom'), defining compatible parsers/players
- Supported Video Codecs: H.264/AVC (primary), H.265/HEVC
- Supported Audio Codecs: AAC (primary), Dolby Digital
- DRM Support: Optional Apple's FairPlay copy protection
- Box Structure: Series of nested boxes with 8-byte headers (4-byte big-endian size including header, 4-byte type); supports boxes like 'moov' (movie metadata), 'mdat' (media data), etc.
- Subtitles/Chapters Support: Yes, via dedicated boxes (e.g., 'trak' for tracks)
These properties are intrinsic as they define how the file is identified, parsed, and handled by file systems and players. File-specific instances (e.g., exact compatible brands) can vary but must conform to the spec for validity.
2. Two Direct Download Links for .M4V Files
Here are two direct download links for sample .M4V files (royalty-free samples for testing):
- https://filesamples.com/samples/video/m4v/sample_1280x720_surfing_with_audio.m4v (1.28 MB, 1280x720 resolution with audio)
- https://filesamples.com/samples/video/m4v/sample_960x400_ocean_with_audio.m4v (604 KB, 960x400 resolution with audio)
3. Ghost Blog Embedded HTML/JavaScript for Drag-and-Drop .M4V Property Dump
This is an embeddable HTML snippet with JavaScript that can be inserted into a Ghost blog post (e.g., via an HTML card). It creates a drag-and-drop area where a user can drop a .M4V file. The script reads the file as an ArrayBuffer, validates it's a .M4V by checking the 'ftyp' signature and 'M4V ' major brand, then dumps all the properties from the list above to the screen (static properties) along with file-extracted details (major brand, minor version, compatible brands) for context.
4. Python Class for .M4V Handling
This Python class opens a .M4V file, decodes the ftyp box to validate and extract dynamic properties (major brand, minor version, compatible brands), prints all properties from the list (static + extracted), and can write the file back (unchanged for simplicity, but modifiable).
import struct
import os
class M4VHandler:
def __init__(self, filepath):
self.filepath = filepath
self.buffer = None
self.major_brand = None
self.minor_version = None
self.compat_brands = []
self.properties = {
'Filename Extension': '.m4v',
'MIME Type': 'video/x-m4v',
'Developer': 'Apple Inc.',
'Container Basis': 'MPEG-4 Part 14',
'File Signature': "offset 4-7 'ftyp'",
'Supported Video Codecs': 'H.264/AVC, H.265/HEVC',
'Supported Audio Codecs': 'AAC, Dolby Digital',
'DRM Support': 'Optional FairPlay',
'Box Structure': 'Series of nested boxes with 8-byte headers',
'Subtitles/Chapters Support': 'Yes'
}
def read_and_decode(self):
with open(self.filepath, 'rb') as f:
self.buffer = f.read()
# Decode ftyp
size = struct.unpack('>I', self.buffer[0:4])[0]
type_ = self.buffer[4:8].decode('ascii')
if type_ != 'ftyp':
raise ValueError('Not a valid .M4V file (missing ftyp)')
self.major_brand = self.buffer[8:12].decode('ascii')
if self.major_brand != 'M4V ':
raise ValueError('Not a standard M4V file (major brand not M4V )')
self.minor_version = struct.unpack('>I', self.buffer[12:16])[0]
for i in range(16, size, 4):
self.compat_brands.append(self.buffer[i:i+4].decode('ascii'))
def print_properties(self):
if not self.major_brand:
self.read_and_decode()
print('Extracted Properties:')
print(f'- Major Brand: {self.major_brand}')
print(f'- Minor Version: {self.minor_version}')
print(f'- Compatible Brands: {", ".join(self.compat_brands)}')
print('\nGeneral Format Properties:')
for key, value in self.properties.items():
print(f'- {key}: {value}')
def write(self, output_path=None):
if not self.buffer:
self.read_and_decode()
if not output_path:
output_path = self.filepath
with open(output_path, 'wb') as f:
f.write(self.buffer)
print(f'File written to {output_path}')
# Example usage:
# handler = M4VHandler('sample.m4v')
# handler.print_properties()
# handler.write('output.m4v')
5. Java Class for .M4V Handling
This Java class does the same: opens, decodes ftyp, prints properties, and writes the file.
import java.io.*;
import java.nio.*;
import java.nio.channels.FileChannel;
import java.util.*;
public class M4VHandler {
private String filepath;
private ByteBuffer buffer;
private String majorBrand;
private int minorVersion;
private List<String> compatBrands = new ArrayList<>();
private Map<String, String> properties = new LinkedHashMap<>();
public M4VHandler(String filepath) {
this.filepath = filepath;
properties.put("Filename Extension", ".m4v");
properties.put("MIME Type", "video/x-m4v");
properties.put("Developer", "Apple Inc.");
properties.put("Container Basis", "MPEG-4 Part 14");
properties.put("File Signature", "offset 4-7 'ftyp'");
properties.put("Supported Video Codecs", "H.264/AVC, H.265/HEVC");
properties.put("Supported Audio Codecs", "AAC, Dolby Digital");
properties.put("DRM Support", "Optional FairPlay");
properties.put("Box Structure", "Series of nested boxes with 8-byte headers");
properties.put("Subtitles/Chapters Support", "Yes");
}
public void readAndDecode() throws IOException {
File file = new File(filepath);
buffer = ByteBuffer.allocate((int) file.length());
try (FileChannel channel = new FileInputStream(file).getChannel()) {
channel.read(buffer);
}
buffer.flip();
// Decode ftyp
int size = buffer.getInt(0);
String type = new String(new byte[]{buffer.get(4), buffer.get(5), buffer.get(6), buffer.get(7)});
if (!"ftyp".equals(type)) {
throw new IllegalArgumentException("Not a valid .M4V file (missing ftyp)");
}
majorBrand = new String(new byte[]{buffer.get(8), buffer.get(9), buffer.get(10), buffer.get(11)});
if (!"M4V ".equals(majorBrand)) {
throw new IllegalArgumentException("Not a standard M4V file (major brand not M4V )");
}
minorVersion = buffer.getInt(12);
for (int i = 16; i < size; i += 4) {
compatBrands.add(new String(new byte[]{buffer.get(i), buffer.get(i+1), buffer.get(i+2), buffer.get(i+3)}));
}
}
public void printProperties() throws IOException {
if (majorBrand == null) {
readAndDecode();
}
System.out.println("Extracted Properties:");
System.out.println("- Major Brand: " + majorBrand);
System.out.println("- Minor Version: " + minorVersion);
System.out.println("- Compatible Brands: " + String.join(", ", compatBrands));
System.out.println("\nGeneral Format Properties:");
for (Map.Entry<String, String> entry : properties.entrySet()) {
System.out.println("- " + entry.getKey() + ": " + entry.getValue());
}
}
public void write(String outputPath) throws IOException {
if (buffer == null) {
readAndDecode();
}
if (outputPath == null) {
outputPath = filepath;
}
try (FileChannel channel = new FileOutputStream(outputPath).getChannel()) {
buffer.position(0);
channel.write(buffer);
}
System.out.println("File written to " + outputPath);
}
// Example usage:
// public static void main(String[] args) throws IOException {
// M4VHandler handler = new M4VHandler("sample.m4v");
// handler.printProperties();
// handler.write("output.m4v");
// }
}
6. JavaScript Class for .M4V Handling
This JavaScript class (for Node.js) opens a file, decodes ftyp, prints properties to console, and writes the file. Requires 'fs' module.
const fs = require('fs');
class M4VHandler {
constructor(filepath) {
this.filepath = filepath;
this.buffer = null;
this.majorBrand = null;
this.minorVersion = null;
this.compatBrands = [];
this.properties = {
'Filename Extension': '.m4v',
'MIME Type': 'video/x-m4v',
'Developer': 'Apple Inc.',
'Container Basis': 'MPEG-4 Part 14',
'File Signature': "offset 4-7 'ftyp'",
'Supported Video Codecs': 'H.264/AVC, H.265/HEVC',
'Supported Audio Codecs': 'AAC, Dolby Digital',
'DRM Support': 'Optional FairPlay',
'Box Structure': 'Series of nested boxes with 8-byte headers',
'Subtitles/Chapters Support': 'Yes'
};
}
readAndDecode() {
this.buffer = fs.readFileSync(this.filepath);
const view = new DataView(this.buffer.buffer);
// Decode ftyp
const size = view.getUint32(0);
const type = String.fromCharCode(view.getUint8(4), view.getUint8(5), view.getUint8(6), view.getUint8(7));
if (type !== 'ftyp') {
throw new Error('Not a valid .M4V file (missing ftyp)');
}
this.majorBrand = String.fromCharCode(view.getUint8(8), view.getUint8(9), view.getUint8(10), view.getUint8(11));
if (this.majorBrand !== 'M4V ') {
throw new Error('Not a standard M4V file (major brand not M4V )');
}
this.minorVersion = view.getUint32(12);
for (let i = 16; i < size; i += 4) {
this.compatBrands.push(String.fromCharCode(view.getUint8(i), view.getUint8(i+1), view.getUint8(i+2), view.getUint8(i+3)));
}
}
printProperties() {
if (!this.majorBrand) {
this.readAndDecode();
}
console.log('Extracted Properties:');
console.log(`- Major Brand: ${this.majorBrand}`);
console.log(`- Minor Version: ${this.minorVersion}`);
console.log(`- Compatible Brands: ${this.compatBrands.join(', ')}`);
console.log('\nGeneral Format Properties:');
for (const [key, value] of Object.entries(this.properties)) {
console.log(`- ${key}: ${value}`);
}
}
write(outputPath = null) {
if (!this.buffer) {
this.readAndDecode();
}
if (!outputPath) {
outputPath = this.filepath;
}
fs.writeFileSync(outputPath, this.buffer);
console.log(`File written to ${outputPath}`);
}
}
// Example usage:
// const handler = new M4VHandler('sample.m4v');
// handler.printProperties();
// handler.write('output.m4v');
7. C++ Class for .M4V Handling
This C++ class (using and ) opens a file, decodes ftyp, prints properties to console, and writes the file.
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <map>
#include <cstdint>
#include <cstring>
class M4VHandler {
private:
std::string filepath;
std::vector<uint8_t> buffer;
std::string major_brand;
uint32_t minor_version;
std::vector<std::string> compat_brands;
std::map<std::string, std::string> properties;
public:
M4VHandler(const std::string& fp) : filepath(fp) {
properties["Filename Extension"] = ".m4v";
properties["MIME Type"] = "video/x-m4v";
properties["Developer"] = "Apple Inc.";
properties["Container Basis"] = "MPEG-4 Part 14";
properties["File Signature"] = "offset 4-7 'ftyp'";
properties["Supported Video Codecs"] = "H.264/AVC, H.265/HEVC";
properties["Supported Audio Codecs"] = "AAC, Dolby Digital";
properties["DRM Support"] = "Optional FairPlay";
properties["Box Structure"] = "Series of nested boxes with 8-byte headers";
properties["Subtitles/Chapters Support"] = "Yes";
}
void read_and_decode() {
std::ifstream file(filepath, std::ios::binary | std::ios::ate);
if (!file) {
throw std::runtime_error("Failed to open file");
}
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
buffer.resize(size);
file.read(reinterpret_cast<char*>(buffer.data()), size);
// Decode ftyp
uint32_t box_size = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
char type[5] = {static_cast<char>(buffer[4]), static_cast<char>(buffer[5]), static_cast<char>(buffer[6]), static_cast<char>(buffer[7]), '\0'};
if (std::strcmp(type, "ftyp") != 0) {
throw std::runtime_error("Not a valid .M4V file (missing ftyp)");
}
char mb[5] = {static_cast<char>(buffer[8]), static_cast<char>(buffer[9]), static_cast<char>(buffer[10]), static_cast<char>(buffer[11]), '\0'};
major_brand = mb;
if (major_brand != "M4V ") {
throw std::runtime_error("Not a standard M4V file (major brand not M4V )");
}
minor_version = (buffer[12] << 24) | (buffer[13] << 16) | (buffer[14] << 8) | buffer[15];
for (uint32_t i = 16; i < box_size; i += 4) {
char cb[5] = {static_cast<char>(buffer[i]), static_cast<char>(buffer[i+1]), static_cast<char>(buffer[i+2]), static_cast<char>(buffer[i+3]), '\0'};
compat_brands.push_back(cb);
}
}
void print_properties() {
if (major_brand.empty()) {
read_and_decode();
}
std::cout << "Extracted Properties:" << std::endl;
std::cout << "- Major Brand: " << major_brand << std::endl;
std::cout << "- Minor Version: " << minor_version << std::endl;
std::cout << "- Compatible Brands: ";
for (size_t i = 0; i < compat_brands.size(); ++i) {
std::cout << compat_brands[i];
if (i < compat_brands.size() - 1) std::cout << ", ";
}
std::cout << std::endl << std::endl << "General Format Properties:" << std::endl;
for (const auto& prop : properties) {
std::cout << "- " << prop.first << ": " << prop.second << std::endl;
}
}
void write(const std::string& output_path = "") {
if (buffer.empty()) {
read_and_decode();
}
std::string out = output_path.empty() ? filepath : output_path;
std::ofstream outfile(out, std::ios::binary);
if (!outfile) {
throw std::runtime_error("Failed to write file");
}
outfile.write(reinterpret_cast<const char*>(buffer.data()), buffer.size());
std::cout << "File written to " << out << std::endl;
}
};
// Example usage:
// int main() {
// try {
// M4VHandler handler("sample.m4v");
// handler.print_properties();
// handler.write("output.m4v");
// } catch (const std::exception& e) {
// std::cerr << e.what() << std::endl;
// }
// return 0;
// }