Task 413: .MP2 File Format
Task 413: .MP2 File Format
- Properties of the .MP2 File Format:
The .MP2 file format (MPEG-1 Audio Layer II) is a lossy audio compression format consisting of a sequence of independent frames, each starting with a 32-bit (4-byte) header followed by audio data. There is no overall file header; properties are defined per frame but are typically constant throughout the file. The format supports MPEG-1 and MPEG-2 (extension) versions, with Layer II encoding. Intrinsic properties are derived from the frame header fields and overall stream characteristics. Below is a comprehensive list based on the ISO/IEC 11172-3 and ISO/IEC 13818-3 specifications:
- Frame Sync: 11 bits set to '11111111111' for synchronization.
- MPEG Version: 2 bits indicating the version.
- '11': MPEG-1
- '10': MPEG-2
- '00': MPEG-2.5 (extension)
- '01': Reserved
- Layer: 2 bits, must be '10' for Layer II.
- Protection (CRC): 1 bit indicating if a 16-bit CRC follows the header.
- '0': Protected (CRC present)
- '1': Not protected
- Bitrate: 4 bits index mapping to bitrate in kbps (varies by MPEG version; certain combinations invalid for stereo/dual channels).
- For MPEG-1 Layer II: 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384 (or 'free' for custom, 'bad' for invalid).
- For MPEG-2/2.5 Layer II: 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256 (or 'free', 'bad').
- Sampling Rate: 2 bits index for frequency in Hz.
- MPEG-1: 44100, 48000, 32000 (or reserved).
- MPEG-2: 22050, 24000, 16000 (or reserved).
- MPEG-2.5: 11025, 12000, 8000 (or reserved).
- Padding: 1 bit indicating if the frame is padded with an extra 8-bit slot to adjust for exact bitrate.
- '0': No padding
- '1': Padded
- Private Bit: 1 bit for application-specific use (informative only).
- Channel Mode: 2 bits defining channels.
- '00': Stereo
- '01': Joint stereo
- '10': Dual channel (two mono)
- '11': Mono (single channel)
- Mode Extension: 2 bits (relevant for joint stereo), defining subband range for intensity stereo.
- '00': Bands 4-31
- '01': Bands 8-31
- '10': Bands 12-31
- '11': Bands 16-31
- Copyright: 1 bit.
- '0': Not copyrighted
- '1': Copyrighted
- Original: 1 bit.
- '0': Copy
- '1': Original media
- Emphasis: 2 bits for de-emphasis filter.
- '00': None
- '01': 50/15 µs
- '10': Reserved
- '11': CCITT J.17
- Frame Size: Calculated as (bitrate * 144000 / sampling_rate) + padding (in bytes for Layer II; slot size is 1 byte).
- Samples per Frame: Fixed at 1152 for Layer II.
- Variable Bitrate (VBR) Support: Allowed via bitrate switching per frame (though not always supported by decoders).
- Channel Support: Up to 2 channels in MPEG-1; up to 5.1 (multichannel) in MPEG-2 extension.
- Compression Type: Sub-band coding with 32 subbands, perceptual model for masking.
- File Extensions: .mp2, .mpa, .m2a.
- MIME Type: audio/mpeg, audio/MPA.
- Overall File Properties (derived): Total frames, duration (total_samples / sampling_rate), average bitrate (if VBR).
These properties are intrinsic to the format's structure and can be extracted by parsing frames.
- Direct Download Links for .MP2 Files:
- https://filesamples.com/samples/audio/mp2/sample1.mp2 (Size: 5.59 MB, Duration: 00:02:02)
- https://filesamples.com/samples/audio/mp2/sample2.mp2 (Size: 9.95 MB, Duration: 00:03:37)
- HTML/JavaScript for Drag-and-Drop .MP2 File Dumper (Embeddable in Ghost Blog):
This HTML/JS can be embedded in a Ghost blog post. It parses the file, extracts properties from each frame (assuming constant for simplicity, but lists per frame if VBR), and dumps them to the screen.
- Python Class for .MP2 Handling:
import struct
import os
class MP2File:
def __init__(self, filename):
self.filename = filename
self.properties = []
self.frames = []
self._parse()
def _parse(self):
with open(self.filename, 'rb') as f:
data = f.read()
offset = 0
while offset < len(data) - 4:
sync = struct.unpack_from('>H', data, offset)[0]
if sync not in (0xFFFC, 0xFFFD): # Layer II sync
offset += 1
continue
header = struct.unpack_from('>I', data, offset)[0]
version_bits = (header >> 19) & 0x03
version = {0b11: 'MPEG-1', 0b10: 'MPEG-2', 0b00: 'MPEG-2.5', 0b01: 'Reserved'}[version_bits]
layer_bits = (header >> 17) & 0x03
if layer_bits != 0b10:
break # Not Layer II
crc = 'Protected' if (header >> 16) & 0x01 == 0 else 'Not protected'
bitrate_idx = (header >> 12) & 0x0F
bitrates_v1 = [0,32,48,56,64,80,96,112,128,160,192,224,256,320,384,0]
bitrates_v2 = [0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,0]
bitrate = bitrates_v1[bitrate_idx] if version == 'MPEG-1' else bitrates_v2[bitrate_idx]
sr_idx = (header >> 10) & 0x03
sr_v1 = [44100,48000,32000,0]
sr_v2 = [22050,24000,16000,0]
sr_v25 = [11025,12000,8000,0]
sr = sr_v1[sr_idx] if version == 'MPEG-1' else sr_v2[sr_idx] if version == 'MPEG-2' else sr_v25[sr_idx]
padding = 'Yes' if (header >> 9) & 0x01 else 'No'
private = 'Set' if (header >> 8) & 0x01 else 'Not set'
channel_bits = (header >> 6) & 0x03
channels = {0b00: 'Stereo', 0b01: 'Joint stereo', 0b10: 'Dual channel', 0b11: 'Mono'}[channel_bits]
mode_ext = (header >> 4) & 0x03
mode_ext_str = ['Bands 4-31', 'Bands 8-31', 'Bands 12-31', 'Bands 16-31'][mode_ext]
copyright = 'Yes' if (header >> 3) & 0x01 else 'No'
original = 'Yes' if (header >> 2) & 0x01 else 'No'
emphasis_bits = header & 0x03
emphasis = {0b00: 'None', 0b01: '50/15 µs', 0b11: 'CCITT J.17', 0b10: 'Reserved'}[emphasis_bits]
props = {
'Version': version, 'CRC': crc, 'Bitrate': bitrate, 'Sampling Rate': sr, 'Padding': padding,
'Private': private, 'Channels': channels, 'Mode Ext': mode_ext_str, 'Copyright': copyright,
'Original': original, 'Emphasis': emphasis
}
self.properties.append(props)
frame_size = (bitrate * 144000 // sr) + ((header >> 9) & 0x01)
self.frames.append(data[offset:offset + frame_size])
offset += frame_size
def print_properties(self):
for i, props in enumerate(self.properties):
print(f"Frame {i+1}:")
for k, v in props.items():
print(f" {k}: {v}")
print(f"Total Frames: {len(self.properties)}")
if self.properties:
total_samples = len(self.properties) * 1152
avg_sr = self.properties[0]['Sampling Rate']
duration = total_samples / avg_sr if avg_sr else 0
print(f"Estimated Duration: {duration:.2f} seconds")
def write(self, output_filename):
# Write the parsed frames back to a new file (unmodified; full encoding not implemented)
with open(output_filename, 'wb') as f:
for frame in self.frames:
f.write(frame)
print(f"File written to {output_filename}")
# Example usage:
# mp2 = MP2File('sample.mp2')
# mp2.print_properties()
# mp2.write('output.mp2')
This class opens a .MP2 file, decodes headers to extract properties per frame, prints them to console, and writes the file (copy) to disk.
- Java Class for .MP2 Handling:
import java.io.*;
import java.nio.*;
import java.util.*;
public class MP2File {
private String filename;
private List<Map<String, Object>> properties = new ArrayList<>();
private List<byte[]> frames = new ArrayList<>();
public MP2File(String filename) {
this.filename = filename;
parse();
}
private void parse() {
try (FileInputStream fis = new FileInputStream(filename);
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
byte[] data = new byte[(int) new File(filename).length()];
fis.read(data);
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.BIG_ENDIAN);
int offset = 0;
while (offset < data.length - 4) {
short sync = bb.getShort(offset);
if (sync != (short)0xFFFC && sync != (short)0xFFFD) {
offset++;
continue;
}
int header = bb.getInt(offset);
int versionBits = (header >> 19) & 0x03;
String version = versionBits == 0b11 ? "MPEG-1" : versionBits == 0b10 ? "MPEG-2" : versionBits == 0b00 ? "MPEG-2.5" : "Reserved";
int layerBits = (header >> 17) & 0x03;
if (layerBits != 0b10) break;
String crc = ((header >> 16) & 0x01) == 0 ? "Protected" : "Not protected";
int bitrateIdx = (header >> 12) & 0x0F;
int[] bitratesV1 = {0,32,48,56,64,80,96,112,128,160,192,224,256,320,384,0};
int[] bitratesV2 = {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,0};
int bitrate = "MPEG-1".equals(version) ? bitratesV1[bitrateIdx] : bitratesV2[bitrateIdx];
int srIdx = (header >> 10) & 0x03;
int[] srV1 = {44100,48000,32000,0};
int[] srV2 = {22050,24000,16000,0};
int[] srV25 = {11025,12000,8000,0};
int sr = "MPEG-1".equals(version) ? srV1[srIdx] : "MPEG-2".equals(version) ? srV2[srIdx] : srV25[srIdx];
String padding = ((header >> 9) & 0x01) == 1 ? "Yes" : "No";
String privateBit = ((header >> 8) & 0x01) == 1 ? "Set" : "Not set";
int channelBits = (header >> 6) & 0x03;
String channels = channelBits == 0b00 ? "Stereo" : channelBits == 0b01 ? "Joint stereo" : channelBits == 0b10 ? "Dual channel" : "Mono";
int modeExt = (header >> 4) & 0x03;
String modeExtStr = new String[]{"Bands 4-31", "Bands 8-31", "Bands 12-31", "Bands 16-31"}[modeExt];
String copyright = ((header >> 3) & 0x01) == 1 ? "Yes" : "No";
String original = ((header >> 2) & 0x01) == 1 ? "Yes" : "No";
int emphasisBits = header & 0x03;
String emphasis = emphasisBits == 0b00 ? "None" : emphasisBits == 0b01 ? "50/15 µs" : emphasisBits == 0b11 ? "CCITT J.17" : "Reserved";
Map<String, Object> props = new HashMap<>();
props.put("Version", version);
props.put("CRC", crc);
props.put("Bitrate", bitrate);
props.put("Sampling Rate", sr);
props.put("Padding", padding);
props.put("Private", privateBit);
props.put("Channels", channels);
props.put("Mode Ext", modeExtStr);
props.put("Copyright", copyright);
props.put("Original", original);
props.put("Emphasis", emphasis);
properties.add(props);
int frameSize = (bitrate * 144000 / sr) + ((header >> 9) & 0x01);
byte[] frame = new byte[frameSize];
System.arraycopy(data, offset, frame, 0, frameSize);
frames.add(frame);
offset += frameSize;
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void printProperties() {
for (int i = 0; i < properties.size(); i++) {
System.out.println("Frame " + (i + 1) + ":");
properties.get(i).forEach((k, v) -> System.out.println(" " + k + ": " + v));
}
System.out.println("Total Frames: " + properties.size());
if (!properties.isEmpty()) {
long totalSamples = (long) properties.size() * 1152;
int avgSr = (int) properties.get(0).get("Sampling Rate");
double duration = avgSr != 0 ? totalSamples / (double) avgSr : 0;
System.out.printf("Estimated Duration: %.2f seconds%n", duration);
}
}
public void write(String outputFilename) throws IOException {
// Write frames back (unmodified)
try (FileOutputStream fos = new FileOutputStream(outputFilename)) {
for (byte[] frame : frames) {
fos.write(frame);
}
}
System.out.println("File written to " + outputFilename);
}
// Example usage:
// public static void main(String[] args) {
// MP2File mp2 = new MP2File("sample.mp2");
// mp2.printProperties();
// mp2.write("output.mp2");
// }
}
This Java class opens a .MP2 file, decodes headers, prints properties to console, and writes the file (copy).
- JavaScript Class for .MP2 Handling:
class MP2File {
constructor(buffer) {
this.buffer = buffer;
this.properties = [];
this.frames = [];
this._parse();
}
_parse() {
const view = new DataView(this.buffer);
let offset = 0;
while (offset < this.buffer.byteLength - 4) {
const sync = view.getUint16(offset);
if (sync !== 0xFFFC && sync !== 0xFFFD) {
offset++;
continue;
}
const header = view.getUint32(offset);
const versionBits = (header >> 19) & 0x03;
const version = versionBits === 0b11 ? 'MPEG-1' : versionBits === 0b10 ? 'MPEG-2' : versionBits === 0b00 ? 'MPEG-2.5' : 'Reserved';
const layerBits = (header >> 17) & 0x03;
if (layerBits !== 0b10) break;
const crc = ((header >> 16) & 0x01) === 0 ? 'Protected' : 'Not protected';
const bitrateIdx = (header >> 12) & 0x0F;
const bitratesV1 = [0,32,48,56,64,80,96,112,128,160,192,224,256,320,384,0];
const bitratesV2 = [0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,0];
const bitrate = version === 'MPEG-1' ? bitratesV1[bitrateIdx] : bitratesV2[bitrateIdx];
const srIdx = (header >> 10) & 0x03;
const srV1 = [44100,48000,32000,0];
const srV2 = [22050,24000,16000,0];
const srV25 = [11025,12000,8000,0];
const sr = version === 'MPEG-1' ? srV1[srIdx] : version === 'MPEG-2' ? srV2[srIdx] : srV25[srIdx];
const padding = ((header >> 9) & 0x01) === 1 ? 'Yes' : 'No';
const privateBit = ((header >> 8) & 0x01) === 1 ? 'Set' : 'Not set';
const channelBits = (header >> 6) & 0x03;
const channels = channelBits === 0b00 ? 'Stereo' : channelBits === 0b01 ? 'Joint stereo' : channelBits === 0b10 ? 'Dual channel' : 'Mono';
const modeExt = (header >> 4) & 0x03;
const modeExtStr = ['Bands 4-31', 'Bands 8-31', 'Bands 12-31', 'Bands 16-31'][modeExt];
const copyright = ((header >> 3) & 0x01) === 1 ? 'Yes' : 'No';
const original = ((header >> 2) & 0x01) === 1 ? 'Yes' : 'No';
const emphasisBits = header & 0x03;
const emphasis = emphasisBits === 0b00 ? 'None' : emphasisBits === 0b01 ? '50/15 µs' : emphasisBits === 0b11 ? 'CCITT J.17' : 'Reserved';
this.properties.push({
Version: version, CRC: crc, Bitrate: bitrate, 'Sampling Rate': sr, Padding: padding,
Private: privateBit, Channels: channels, 'Mode Ext': modeExtStr, Copyright: copyright,
Original: original, Emphasis: emphasis
});
const frameSize = Math.floor((bitrate * 144000) / sr) + ((header >> 9) & 0x01);
const frame = this.buffer.slice(offset, offset + frameSize);
this.frames.push(frame);
offset += frameSize;
}
}
printProperties() {
this.properties.forEach((props, i) => {
console.log(`Frame ${i + 1}:`);
Object.entries(props).forEach(([k, v]) => console.log(` ${k}: ${v}`));
});
console.log(`Total Frames: ${this.properties.length}`);
if (this.properties.length > 0) {
const totalSamples = this.properties.length * 1152;
const avgSr = this.properties[0]['Sampling Rate'];
const duration = avgSr ? totalSamples / avgSr : 0;
console.log(`Estimated Duration: ${duration.toFixed(2)} seconds`);
}
}
write() {
// Write to blob (unmodified); can be saved via download
const blob = new Blob(this.frames, { type: 'audio/mp2' });
const url = URL.createObjectURL(blob);
console.log(`Download URL: ${url}`);
// To save: const a = document.createElement('a'); a.href = url; a.download = 'output.mp2'; a.click();
return blob;
}
}
// Example usage (in browser with FileReader):
// const reader = new FileReader();
// reader.onload = (e) => {
// const mp2 = new MP2File(e.target.result);
// mp2.printProperties();
// mp2.write();
// };
// reader.readAsArrayBuffer(file);
This JS class takes an ArrayBuffer, decodes headers, prints properties to console, and creates a Blob for writing/saving.
- C Implementation for .MP2 Handling (Using Struct and Functions, as C Has No Classes):
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
typedef struct {
char *version;
char *crc;
int bitrate;
int sampling_rate;
char *padding;
char *private_bit;
char *channels;
char *mode_ext;
char *copyright;
char *original;
char *emphasis;
} MP2Properties;
typedef struct {
char *filename;
MP2Properties *properties;
uint8_t **frames;
int frame_count;
size_t *frame_sizes;
} MP2File;
MP2File* mp2_open(const char *filename) {
MP2File *mp2 = malloc(sizeof(MP2File));
mp2->filename = strdup(filename);
mp2->properties = NULL;
mp2->frames = NULL;
mp2->frame_count = 0;
mp2->frame_sizes = NULL;
FILE *f = fopen(filename, "rb");
if (!f) return NULL;
fseek(f, 0, SEEK_END);
long len = ftell(f);
fseek(f, 0, SEEK_SET);
uint8_t *data = malloc(len);
fread(data, 1, len, f);
fclose(f);
int offset = 0;
while (offset < len - 4) {
uint16_t sync = (data[offset] << 8) | data[offset + 1];
if (sync != 0xFFFC && sync != 0xFFFD) {
offset++;
continue;
}
uint32_t header = (data[offset] << 24) | (data[offset+1] << 16) | (data[offset+2] << 8) | data[offset+3];
uint32_t version_bits = (header >> 19) & 0x03;
char *version = version_bits == 0b11 ? "MPEG-1" : version_bits == 0b10 ? "MPEG-2" : version_bits == 0b00 ? "MPEG-2.5" : "Reserved";
uint32_t layer_bits = (header >> 17) & 0x03;
if (layer_bits != 0b10) break;
char *crc = ((header >> 16) & 0x01) == 0 ? "Protected" : "Not protected";
uint32_t bitrate_idx = (header >> 12) & 0x0F;
int bitrates_v1[16] = {0,32,48,56,64,80,96,112,128,160,192,224,256,320,384,0};
int bitrates_v2[16] = {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,0};
int bitrate = strcmp(version, "MPEG-1") == 0 ? bitrates_v1[bitrate_idx] : bitrates_v2[bitrate_idx];
uint32_t sr_idx = (header >> 10) & 0x03;
int sr_v1[4] = {44100,48000,32000,0};
int sr_v2[4] = {22050,24000,16000,0};
int sr_v25[4] = {11025,12000,8000,0};
int sr = strcmp(version, "MPEG-1") == 0 ? sr_v1[sr_idx] : strcmp(version, "MPEG-2") == 0 ? sr_v2[sr_idx] : sr_v25[sr_idx];
char *padding = ((header >> 9) & 0x01) ? "Yes" : "No";
char *private_bit = ((header >> 8) & 0x01) ? "Set" : "Not set";
uint32_t channel_bits = (header >> 6) & 0x03;
char *channels = channel_bits == 0b00 ? "Stereo" : channel_bits == 0b01 ? "Joint stereo" : channel_bits == 0b10 ? "Dual channel" : "Mono";
uint32_t mode_ext_idx = (header >> 4) & 0x03;
char *mode_ext_arr[4] = {"Bands 4-31", "Bands 8-31", "Bands 12-31", "Bands 16-31"};
char *mode_ext = mode_ext_arr[mode_ext_idx];
char *copyright = ((header >> 3) & 0x01) ? "Yes" : "No";
char *original = ((header >> 2) & 0x01) ? "Yes" : "No";
uint32_t emphasis_bits = header & 0x03;
char *emphasis = emphasis_bits == 0b00 ? "None" : emphasis_bits == 0b01 ? "50/15 us" : emphasis_bits == 0b11 ? "CCITT J.17" : "Reserved";
mp2->properties = realloc(mp2->properties, sizeof(MP2Properties) * (mp2->frame_count + 1));
MP2Properties *props = &mp2->properties[mp2->frame_count];
props->version = strdup(version);
props->crc = strdup(crc);
props->bitrate = bitrate;
props->sampling_rate = sr;
props->padding = strdup(padding);
props->private_bit = strdup(private_bit);
props->channels = strdup(channels);
props->mode_ext = strdup(mode_ext);
props->copyright = strdup(copyright);
props->original = strdup(original);
props->emphasis = strdup(emphasis);
int frame_size = (bitrate * 144000 / sr) + ((header >> 9) & 0x01);
mp2->frames = realloc(mp2->frames, sizeof(uint8_t*) * (mp2->frame_count + 1));
mp2->frames[mp2->frame_count] = malloc(frame_size);
memcpy(mp2->frames[mp2->frame_count], data + offset, frame_size);
mp2->frame_sizes = realloc(mp2->frame_sizes, sizeof(size_t) * (mp2->frame_count + 1));
mp2->frame_sizes[mp2->frame_count] = frame_size;
offset += frame_size;
mp2->frame_count++;
}
free(data);
return mp2;
}
void mp2_print_properties(MP2File *mp2) {
for (int i = 0; i < mp2->frame_count; i++) {
MP2Properties *p = &mp2->properties[i];
printf("Frame %d:\n", i + 1);
printf(" Version: %s\n", p->version);
printf(" CRC: %s\n", p->crc);
printf(" Bitrate: %d\n", p->bitrate);
printf(" Sampling Rate: %d\n", p->sampling_rate);
printf(" Padding: %s\n", p->padding);
printf(" Private: %s\n", p->private_bit);
printf(" Channels: %s\n", p->channels);
printf(" Mode Ext: %s\n", p->mode_ext);
printf(" Copyright: %s\n", p->copyright);
printf(" Original: %s\n", p->original);
printf(" Emphasis: %s\n", p->emphasis);
}
printf("Total Frames: %d\n", mp2->frame_count);
if (mp2->frame_count > 0) {
long total_samples = (long)mp2->frame_count * 1152;
int avg_sr = mp2->properties[0].sampling_rate;
double duration = avg_sr ? (double)total_samples / avg_sr : 0;
printf("Estimated Duration: %.2f seconds\n", duration);
}
}
void mp2_write(MP2File *mp2, const char *output_filename) {
FILE *f = fopen(output_filename, "wb");
if (!f) return;
for (int i = 0; i < mp2->frame_count; i++) {
fwrite(mp2->frames[i], 1, mp2->frame_sizes[i], f);
}
fclose(f);
printf("File written to %s\n", output_filename);
}
void mp2_close(MP2File *mp2) {
for (int i = 0; i < mp2->frame_count; i++) {
free(mp2->properties[i].version);
free(mp2->properties[i].crc);
free(mp2->properties[i].padding);
free(mp2->properties[i].private_bit);
free(mp2->properties[i].channels);
free(mp2->properties[i].mode_ext);
free(mp2->properties[i].copyright);
free(mp2->properties[i].original);
free(mp2->properties[i].emphasis);
free(mp2->frames[i]);
}
free(mp2->properties);
free(mp2->frames);
free(mp2->frame_sizes);
free(mp2->filename);
free(mp2);
}
// Example usage:
// int main() {
// MP2File *mp2 = mp2_open("sample.mp2");
// if (mp2) {
// mp2_print_properties(mp2);
// mp2_write(mp2, "output.mp2");
// mp2_close(mp2);
// }
// return 0;
// }
This C implementation uses a struct to mimic a class, opens a .MP2 file, decodes headers, prints properties to console, and writes the file (copy).