Task 616: .RMVB File Format

Task 616: .RMVB File Format

1. List of Properties Intrinsic to the .RMVB File Format

The .RMVB file format is a variable bitrate variant of the RealMedia (.RM) container format. It uses the same chunk-based structure as RealMedia, with no structural differences beyond the encoding method (VBR for content). The format is big-endian and consists of chunks (each with a FOURCC type, size, version, and payload). The intrinsic properties are the metadata fields extracted from the mandatory and optional headers (chunks). These are not file system attributes (like permissions or timestamps) but format-specific fields defining the container's structure, streams, and content.

Based on the RealMedia File Format (RMFF) specification, the key properties are:

From RealMedia File Header (.RMF chunk):

  • File Version (dword): The version of the file (typically 0 or 1).
  • Number of Headers (dword): The count of header chunks following the .RMF.

From File Properties Header (PROP chunk):

  • Max Bit Rate (dword): Maximum bitrate of the file in bits per second.
  • Avg Bit Rate (dword): Average bitrate of the file in bits per second.
  • Max Packet Size (dword): Size of the largest data packet in bytes.
  • Avg Packet Size (dword): Average size of data packets in bytes.
  • Num Packets (dword): Total number of data packets in the file.
  • Duration (dword): Total duration of the file in milliseconds.
  • Preroll (dword): Suggested buffering time in milliseconds before playback.
  • Index Offset (dword): Offset to the first INDX chunk from the file start.
  • Data Offset (dword): Offset to the first DATA chunk from the file start.
  • Num Streams (word): Number of streams in the file.
  • Flags (word): Bitfield indicating file capabilities (bit 0: savable; bit 1: supports perfect play/extra buffering; bit 2: live broadcast).

From Content Description Header (CONT chunk, optional):

  • Title (string): Title of the content (length-prefixed).
  • Author (string): Author of the content (length-prefixed).
  • Copyright (string): Copyright information (length-prefixed).
  • Comment (string): Additional comments (length-prefixed).

From Media Properties Header (MDPR chunk, one per stream):

  • Stream Number (word): Identifier for the stream (e.g., 0 for video, 1 for audio).
  • Max Bit Rate (dword): Maximum bitrate for the stream.
  • Avg Bit Rate (dword): Average bitrate for the stream.
  • Max Packet Size (dword): Largest packet size for the stream.
  • Avg Packet Size (dword): Average packet size for the stream.
  • Start Time (dword): Start offset of the stream in milliseconds.
  • Preroll (dword): Preroll time for the stream in milliseconds.
  • Duration (dword): Duration of the stream in milliseconds.
  • Stream Name (string): Descriptive name of the stream (length-prefixed).
  • Mime Type (string): MIME type of the stream (e.g., video/x-pn-realvideo, audio/x-pn-realaudio; length-prefixed).
  • Type Specific Data (binary): Codec-specific data (variable length; e.g., for audio, includes codec FourCC, sample rate, channels; for video, width, height, etc.).

Other chunks like DATA (media packets) and INDX (index entries) contain runtime data but no additional intrinsic metadata properties. Type-specific data varies by mime type and codec (e.g., FourCC like RV40 for video, cook for audio) and may include sub-fields like sample rate, channels, or dimensions, but these are embedded binary blobs.

3. Ghost Blog Embedded HTML/JavaScript for Drag-and-Drop .RMVB Property Dump

This is a self-contained HTML page with JavaScript that allows dragging and dropping a .RMVB file. It uses FileReader to parse the file as ArrayBuffer, reads the big-endian chunks, extracts the properties from .RMF, PROP, CONT, and MDPR chunks, and dumps them to the screen in a pre-formatted block. It does not support writing.

RMVB Property Dumper

Drag and Drop .RMVB File to Dump Properties

Drop .RMVB file here

4. Python Class for .RMVB File Handling

This Python class uses built-in struct for big-endian parsing. It can open a file, read and decode properties, print them to console, and write a new file with modified properties (basic implementation: reads the original, modifies headers, keeps data intact).

import struct
import binascii

class RMVBFile:
    def __init__(self, filepath):
        self.filepath = filepath
        self.properties = {}
        self.data_chunks = []  # For writing
        self.other_chunks = []  # For unknown chunks

    def read_dword(self, f):
        return struct.unpack('>I', f.read(4))[0]

    def read_word(self, f):
        return struct.unpack('>H', f.read(2))[0]

    def read_byte(self, f):
        return struct.unpack('>B', f.read(1))[0]

    def read_string(self, f, len):
        return f.read(len).decode('utf-8', errors='ignore')

    def read_binary(self, f, len):
        return f.read(len)

    def decode(self):
        with open(self.filepath, 'rb') as f:
            while True:
                try:
                    type = f.read(4).decode('ascii')
                    if not type:
                        break
                    size = self.read_dword(f)
                    version = self.read_word(f)
                    if type == '.RMF':
                        self.properties['file_version'] = self.read_dword(f)
                        self.properties['num_headers'] = self.read_dword(f)
                    elif type == 'PROP':
                        self.properties['max_bit_rate'] = self.read_dword(f)
                        self.properties['avg_bit_rate'] = self.read_dword(f)
                        self.properties['max_packet_size'] = self.read_dword(f)
                        self.properties['avg_packet_size'] = self.read_dword(f)
                        self.properties['num_packets'] = self.read_dword(f)
                        self.properties['duration'] = self.read_dword(f)
                        self.properties['preroll'] = self.read_dword(f)
                        self.properties['index_offset'] = self.read_dword(f)
                        self.properties['data_offset'] = self.read_dword(f)
                        self.properties['num_streams'] = self.read_word(f)
                        flags = self.read_word(f)
                        self.properties['flags'] = flags
                        self.properties['save_allowed'] = bool(flags & 1)
                        self.properties['perfect_play'] = bool(flags & 2)
                        self.properties['live_broadcast'] = bool(flags & 4)
                    elif type == 'CONT':
                        self.properties['title'] = self.read_string(f, self.read_word(f))
                        self.properties['author'] = self.read_string(f, self.read_word(f))
                        self.properties['copyright'] = self.read_string(f, self.read_word(f))
                        self.properties['comment'] = self.read_string(f, self.read_word(f))
                    elif type == 'MDPR':
                        stream_num = self.read_word(f)
                        stream_key = f'stream_{stream_num}'
                        self.properties[stream_key] = {}
                        self.properties[stream_key]['max_bit_rate'] = self.read_dword(f)
                        self.properties[stream_key]['avg_bit_rate'] = self.read_dword(f)
                        self.properties[stream_key]['max_packet_size'] = self.read_dword(f)
                        self.properties[stream_key]['avg_packet_size'] = self.read_dword(f)
                        self.properties[stream_key]['start_time'] = self.read_dword(f)
                        self.properties[stream_key]['preroll'] = self.read_dword(f)
                        self.properties[stream_key]['duration'] = self.read_dword(f)
                        self.properties[stream_key]['stream_name'] = self.read_string(f, self.read_byte(f))
                        self.properties[stream_key]['mime_type'] = self.read_string(f, self.read_byte(f))
                        ts_len = self.read_dword(f)
                        self.properties[stream_key]['type_specific_data'] = binascii.hexlify(self.read_binary(f, ts_len)).decode()
                    elif type == 'DATA':
                        # Store for writing
                        num_packets = self.read_dword(f)
                        next_data = self.read_dword(f)
                        data_payload = f.read(size - 18)
                        self.data_chunks.append((type, size, version, num_packets, next_data, data_payload))
                    else:
                        # Store unknown for writing
                        payload = f.read(size - 10)
                        self.other_chunks.append((type, size, version, payload))
                except:
                    break

    def print_properties(self):
        for key, value in self.properties.items():
            if isinstance(value, dict):
                print(f'{key}:')
                for sub_key, sub_value in value.items():
                    print(f'  {sub_key}: {sub_value}')
            else:
                print(f'{key}: {value}')

    def write(self, new_filepath, modifications=None):
        if modifications:
            self.properties.update(modifications)
        with open(new_filepath, 'wb') as f:
            # Write .RMF
            f.write(b'.RMF')
            f.write(struct.pack('>I', 18))  # Size
            f.write(struct.pack('>H', 0))  # Version
            f.write(struct.pack('>I', self.properties.get('file_version', 0)))
            f.write(struct.pack('>I', self.properties.get('num_headers', 0)))

            # Write PROP
            f.write(b'PROP')
            f.write(struct.pack('>I', 50))  # Size
            f.write(struct.pack('>H', 0))
            f.write(struct.pack('>I', self.properties.get('max_bit_rate', 0)))
            f.write(struct.pack('>I', self.properties.get('avg_bit_rate', 0)))
            f.write(struct.pack('>I', self.properties.get('max_packet_size', 0)))
            f.write(struct.pack('>I', self.properties.get('avg_packet_size', 0)))
            f.write(struct.pack('>I', self.properties.get('num_packets', 0)))
            f.write(struct.pack('>I', self.properties.get('duration', 0)))
            f.write(struct.pack('>I', self.properties.get('preroll', 0)))
            f.write(struct.pack('>I', self.properties.get('index_offset', 0)))
            f.write(struct.pack('>I', self.properties.get('data_offset', 0)))
            f.write(struct.pack('>H', self.properties.get('num_streams', 0)))
            flags = self.properties.get('flags', 0)
            f.write(struct.pack('>H', flags))

            # Write CONT if present
            if 'title' in self.properties:
                title = self.properties['title'].encode('utf-8')
                author = self.properties['author'].encode('utf-8')
                copyright = self.properties['copyright'].encode('utf-8')
                comment = self.properties['comment'].encode('utf-8')
                cont_size = 10 + 8 + len(title) + len(author) + len(copyright) + len(comment)
                f.write(b'CONT')
                f.write(struct.pack('>I', cont_size))
                f.write(struct.pack('>H', 0))
                f.write(struct.pack('>H', len(title)))
                f.write(title)
                f.write(struct.pack('>H', len(author)))
                f.write(author)
                f.write(struct.pack('>H', len(copyright)))
                f.write(copyright)
                f.write(struct.pack('>H', len(comment)))
                f.write(comment)

            # Write MDPR (simplified, assuming one stream for example)
            for key in self.properties:
                if key.startswith('stream_'):
                    stream = self.properties[key]
                    name = stream['stream_name'].encode('utf-8')
                    mime = stream['mime_type'].encode('utf-8')
                    ts_data = binascii.unhexlify(stream['type_specific_data'])
                    mdpr_size = 10 + 38 + len(name) + 1 + len(mime) + 1 + 4 + len(ts_data)
                    f.write(b'MDPR')
                    f.write(struct.pack('>I', mdpr_size))
                    f.write(struct.pack('>H', 0))
                    stream_num = int(key.split('_')[1])
                    f.write(struct.pack('>H', stream_num))
                    f.write(struct.pack('>I', stream['max_bit_rate']))
                    f.write(struct.pack('>I', stream['avg_bit_rate']))
                    f.write(struct.pack('>I', stream['max_packet_size']))
                    f.write(struct.pack('>I', stream['avg_packet_size']))
                    f.write(struct.pack('>I', stream['start_time']))
                    f.write(struct.pack('>I', stream['preroll']))
                    f.write(struct.pack('>I', stream['duration']))
                    f.write(struct.pack('>B', len(name)))
                    f.write(name)
                    f.write(struct.pack('>B', len(mime)))
                    f.write(mime)
                    f.write(struct.pack('>I', len(ts_data)))
                    f.write(ts_data)

            # Write other chunks
            for type, size, version, payload in self.other_chunks:
                f.write(type.encode('ascii'))
                f.write(struct.pack('>I', size))
                f.write(struct.pack('>H', version))
                f.write(payload)

            # Write DATA chunks
            for type, size, version, num_packets, next_data, payload in self.data_chunks:
                f.write(b'DATA')
                f.write(struct.pack('>I', size))
                f.write(struct.pack('>H', version))
                f.write(struct.pack('>I', num_packets))
                f.write(struct.pack('>I', next_data))
                f.write(payload)

# Example usage
# rmvb = RMVBFile('sample.rmvb')
# rmvb.decode()
# rmvb.print_properties()
# rmvb.write('modified.rmvb', {'duration': 100000})

5. Java Class for .RMVB File Handling

This Java class uses ByteBuffer for big-endian parsing. It reads, decodes, prints properties, and writes a modified file.

import java.io.*;
import java.nio.*;
import java.nio.channels.FileChannel;
import java.util.*;

public class RMVBFile {
    private String filepath;
    private Map<String, Object> properties = new HashMap<>();
    private List<byte[]> dataChunks = new ArrayList<>(); // For writing
    private List<byte[]> otherChunks = new ArrayList<>();

    public RMVBFile(String filepath) {
        this.filepath = filepath;
    }

    private int readDWord(ByteBuffer bb) {
        return bb.getInt();
    }

    private short readWord(ByteBuffer bb) {
        return bb.getShort();
    }

    private byte readByte(ByteBuffer bb) {
        return bb.get();
    }

    private String readString(ByteBuffer bb, int len) {
        byte[] bytes = new byte[len];
        bb.get(bytes);
        return new String(bytes);
    }

    private byte[] readBinary(ByteBuffer bb, int len) {
        byte[] bytes = new byte[len];
        bb.get(bytes);
        return bytes;
    }

    public void decode() throws IOException {
        File file = new File(filepath);
        ByteBuffer bb = ByteBuffer.allocate((int) file.length()).order(ByteOrder.BIG_ENDIAN);
        try (FileChannel fc = new FileInputStream(file).getChannel()) {
            fc.read(bb);
            bb.flip();
        }
        while (bb.hasRemaining()) {
            byte[] typeBytes = new byte[4];
            bb.get(typeBytes);
            String type = new String(typeBytes);
            int size = readDWord(bb);
            short version = readWord(bb);
            if (type.equals(".RMF")) {
                properties.put("file_version", readDWord(bb));
                properties.put("num_headers", readDWord(bb));
            } else if (type.equals("PROP")) {
                properties.put("max_bit_rate", readDWord(bb));
                properties.put("avg_bit_rate", readDWord(bb));
                properties.put("max_packet_size", readDWord(bb));
                properties.put("avg_packet_size", readDWord(bb));
                properties.put("num_packets", readDWord(bb));
                properties.put("duration", readDWord(bb));
                properties.put("preroll", readDWord(bb));
                properties.put("index_offset", readDWord(bb));
                properties.put("data_offset", readDWord(bb));
                properties.put("num_streams", (int) readWord(bb));
                int flags = readWord(bb);
                properties.put("flags", flags);
                properties.put("save_allowed", (flags & 1) != 0);
                properties.put("perfect_play", (flags & 2) != 0);
                properties.put("live_broadcast", (flags & 4) != 0);
            } else if (type.equals("CONT")) {
                properties.put("title", readString(bb, readWord(bb)));
                properties.put("author", readString(bb, readWord(bb)));
                properties.put("copyright", readString(bb, readWord(bb)));
                properties.put("comment", readString(bb, readWord(bb)));
            } else if (type.equals("MDPR")) {
                short streamNum = readWord(bb);
                String streamKey = "stream_" + streamNum;
                Map<String, Object> streamProps = new HashMap<>();
                streamProps.put("max_bit_rate", readDWord(bb));
                streamProps.put("avg_bit_rate", readDWord(bb));
                streamProps.put("max_packet_size", readDWord(bb));
                streamProps.put("avg_packet_size", readDWord(bb));
                streamProps.put("start_time", readDWord(bb));
                streamProps.put("preroll", readDWord(bb));
                streamProps.put("duration", readDWord(bb));
                streamProps.put("stream_name", readString(bb, readByte(bb)));
                streamProps.put("mime_type", readString(bb, readByte(bb)));
                int tsLen = readDWord(bb);
                streamProps.put("type_specific_data", toHex(readBinary(bb, tsLen)));
                properties.put(streamKey, streamProps);
            } else if (type.equals("DATA")) {
                byte[] chunk = new byte[size];
                bb.position(bb.position() - 10);
                bb.get(chunk);
                dataChunks.add(chunk);
            } else {
                byte[] chunk = new byte[size];
                bb.position(bb.position() - 10);
                bb.get(chunk);
                otherChunks.add(chunk);
            }
        }
    }

    private String toHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) sb.append(String.format("%02x ", b));
        return sb.toString().trim();
    }

    public void printProperties() {
        for (Map.Entry<String, Object> entry : properties.entrySet()) {
            if (entry.getValue() instanceof Map) {
                System.out.println(entry.getKey() + ":");
                @SuppressWarnings("unchecked")
                Map<String, Object> sub = (Map<String, Object>) entry.getValue();
                for (Map.Entry<String, Object> subEntry : sub.entrySet()) {
                    System.out.println("  " + subEntry.getKey() + ": " + subEntry.getValue());
                }
            } else {
                System.out.println(entry.getKey() + ": " + entry.getValue());
            }
        }
    }

    public void write(String newFilepath, Map<String, Object> modifications) throws IOException {
        if (modifications != null) {
            properties.putAll(modifications);
        }
        ByteBuffer bb = ByteBuffer.allocate(1024 * 1024).order(ByteOrder.BIG_ENDIAN); // Adjustable buffer

        // Write .RMF
        bb.put(".RMF".getBytes());
        bb.putInt(18);
        bb.putShort((short) 0);
        bb.putInt((Integer) properties.getOrDefault("file_version", 0));
        bb.putInt((Integer) properties.getOrDefault("num_headers", 0));

        // Write PROP
        bb.put("PROP".getBytes());
        bb.putInt(50);
        bb.putShort((short) 0);
        bb.putInt((Integer) properties.getOrDefault("max_bit_rate", 0));
        bb.putInt((Integer) properties.getOrDefault("avg_bit_rate", 0));
        bb.putInt((Integer) properties.getOrDefault("max_packet_size", 0));
        bb.putInt((Integer) properties.getOrDefault("avg_packet_size", 0));
        bb.putInt((Integer) properties.getOrDefault("num_packets", 0));
        bb.putInt((Integer) properties.getOrDefault("duration", 0));
        bb.putInt((Integer) properties.getOrDefault("preroll", 0));
        bb.putInt((Integer) properties.getOrDefault("index_offset", 0));
        bb.putInt((Integer) properties.getOrDefault("data_offset", 0));
        bb.putShort((short) (int) properties.getOrDefault("num_streams", 0));
        bb.putShort((short) (int) properties.getOrDefault("flags", 0));

        // Write CONT if present
        if (properties.containsKey("title")) {
            String title = (String) properties.get("title");
            String author = (String) properties.get("author");
            String copyright = (String) properties.get("copyright");
            String comment = (String) properties.get("comment");
            int contSize = 10 + 8 + title.length() + author.length() + copyright.length() + comment.length();
            bb.put("CONT".getBytes());
            bb.putInt(contSize);
            bb.putShort((short) 0);
            bb.putShort((short) title.length());
            bb.put(title.getBytes());
            bb.putShort((short) author.length());
            bb.put(author.getBytes());
            bb.putShort((short) copyright.length());
            bb.put(copyright.getBytes());
            bb.putShort((short) comment.length());
            bb.put(comment.getBytes());
        }

        // Write MDPR (example for streams)
        for (Map.Entry<String, Object> entry : properties.entrySet()) {
            if (entry.getKey().startsWith("stream_")) {
                @SuppressWarnings("unchecked")
                Map<String, Object> stream = (Map<String, Object>) entry.getValue();
                String name = (String) stream.get("stream_name");
                String mime = (String) stream.get("mime_type");
                byte[] tsData = fromHex((String) stream.get("type_specific_data"));
                int mdprSize = 10 + 38 + 1 + name.length() + 1 + mime.length() + 4 + tsData.length;
                bb.put("MDPR".getBytes());
                bb.putInt(mdprSize);
                bb.putShort((short) 0);
                short streamNum = Short.parseShort(entry.getKey().split("_")[1]);
                bb.putShort(streamNum);
                bb.putInt((Integer) stream.get("max_bit_rate"));
                bb.putInt((Integer) stream.get("avg_bit_rate"));
                bb.putInt((Integer) stream.get("max_packet_size"));
                bb.putInt((Integer) stream.get("avg_packet_size"));
                bb.putInt((Integer) stream.get("start_time"));
                bb.putInt((Integer) stream.get("preroll"));
                bb.putInt((Integer) stream.get("duration"));
                bb.put((byte) name.length());
                bb.put(name.getBytes());
                bb.put((byte) mime.length());
                bb.put(mime.getBytes());
                bb.putInt(tsData.length);
                bb.put(tsData);
            }
        }

        // Write other chunks
        for (byte[] chunk : otherChunks) {
            bb.put(chunk);
        }

        // Write data chunks
        for (byte[] chunk : dataChunks) {
            bb.put(chunk);
        }

        bb.flip();
        try (FileChannel fc = new FileOutputStream(newFilepath).getChannel()) {
            fc.write(bb);
        }
    }

    private byte[] fromHex(String hex) {
        String[] hexes = hex.split(" ");
        byte[] bytes = new byte[hexes.length];
        for (int i = 0; i < hexes.length; i++) {
            bytes[i] = (byte) Integer.parseInt(hexes[i], 16);
        }
        return bytes;
    }

    // Example usage
    // public static void main(String[] args) throws IOException {
    //     RMVBFile rmvb = new RMVBFile("sample.rmvb");
    //     rmvb.decode();
    //     rmvb.printProperties();
    //     Map<String, Object> mods = new HashMap<>();
    //     mods.put("duration", 100000);
    //     rmvb.write("modified.rmvb", mods);
    // }
}

6. JavaScript Class for .RMVB File Handling

This JavaScript class (for Node.js) uses fs and Buffer for parsing. It reads, decodes, prints to console, and writes modified files. Run with Node.js.

const fs = require('fs');

class RMVBFile {
    constructor(filepath) {
        this.filepath = filepath;
        this.properties = {};
        this.dataChunks = [];
        this.otherChunks = [];
    }

    readDWord(buffer, offset) {
        return buffer.readUInt32BE(offset);
    }

    readWord(buffer, offset) {
        return buffer.readUInt16BE(offset);
    }

    readByte(buffer, offset) {
        return buffer.readUInt8(offset);
    }

    readString(buffer, offset, len) {
        return buffer.toString('utf8', offset, offset + len);
    }

    readBinary(buffer, offset, len) {
        return buffer.slice(offset, offset + len).toString('hex');
    }

    decode() {
        const buffer = fs.readFileSync(this.filepath);
        let offset = 0;
        while (offset < buffer.length) {
            const type = buffer.toString('ascii', offset, offset + 4);
            offset += 4;
            const size = this.readDWord(buffer, offset);
            offset += 4;
            const version = this.readWord(buffer, offset);
            offset += 2;
            if (type === '.RMF') {
                this.properties.file_version = this.readDWord(buffer, offset);
                offset += 4;
                this.properties.num_headers = this.readDWord(buffer, offset);
                offset += 4;
            } else if (type === 'PROP') {
                this.properties.max_bit_rate = this.readDWord(buffer, offset);
                offset += 4;
                this.properties.avg_bit_rate = this.readDWord(buffer, offset);
                offset += 4;
                this.properties.max_packet_size = this.readDWord(buffer, offset);
                offset += 4;
                this.properties.avg_packet_size = this.readDWord(buffer, offset);
                offset += 4;
                this.properties.num_packets = this.readDWord(buffer, offset);
                offset += 4;
                this.properties.duration = this.readDWord(buffer, offset);
                offset += 4;
                this.properties.preroll = this.readDWord(buffer, offset);
                offset += 4;
                this.properties.index_offset = this.readDWord(buffer, offset);
                offset += 4;
                this.properties.data_offset = this.readDWord(buffer, offset);
                offset += 4;
                this.properties.num_streams = this.readWord(buffer, offset);
                offset += 2;
                const flags = this.readWord(buffer, offset);
                offset += 2;
                this.properties.flags = flags;
                this.properties.save_allowed = !!(flags & 1);
                this.properties.perfect_play = !!(flags & 2);
                this.properties.live_broadcast = !!(flags & 4);
            } else if (type === 'CONT') {
                const titleLen = this.readWord(buffer, offset);
                offset += 2;
                this.properties.title = this.readString(buffer, offset, titleLen);
                offset += titleLen;
                const authorLen = this.readWord(buffer, offset);
                offset += 2;
                this.properties.author = this.readString(buffer, offset, authorLen);
                offset += authorLen;
                const copyrightLen = this.readWord(buffer, offset);
                offset += 2;
                this.properties.copyright = this.readString(buffer, offset, copyrightLen);
                offset += copyrightLen;
                const commentLen = this.readWord(buffer, offset);
                offset += 2;
                this.properties.comment = this.readString(buffer, offset, commentLen);
                offset += commentLen;
            } else if (type === 'MDPR') {
                const streamNum = this.readWord(buffer, offset);
                offset += 2;
                const streamKey = `stream_${streamNum}`;
                this.properties[streamKey] = {};
                this.properties[streamKey].max_bit_rate = this.readDWord(buffer, offset);
                offset += 4;
                this.properties[streamKey].avg_bit_rate = this.readDWord(buffer, offset);
                offset += 4;
                this.properties[streamKey].max_packet_size = this.readDWord(buffer, offset);
                offset += 4;
                this.properties[streamKey].avg_packet_size = this.readDWord(buffer, offset);
                offset += 4;
                this.properties[streamKey].start_time = this.readDWord(buffer, offset);
                offset += 4;
                this.properties[streamKey].preroll = this.readDWord(buffer, offset);
                offset += 4;
                this.properties[streamKey].duration = this.readDWord(buffer, offset);
                offset += 4;
                const nameLen = this.readByte(buffer, offset);
                offset += 1;
                this.properties[streamKey].stream_name = this.readString(buffer, offset, nameLen);
                offset += nameLen;
                const mimeLen = this.readByte(buffer, offset);
                offset += 1;
                this.properties[streamKey].mime_type = this.readString(buffer, offset, mimeLen);
                offset += mimeLen;
                const tsLen = this.readDWord(buffer, offset);
                offset += 4;
                this.properties[streamKey].type_specific_data = this.readBinary(buffer, offset, tsLen);
                offset += tsLen;
            } else if (type === 'DATA') {
                this.dataChunks.push(buffer.slice(offset - 10, offset - 10 + size));
                offset += size - 10;
            } else {
                this.otherChunks.push(buffer.slice(offset - 10, offset - 10 + size));
                offset += size - 10;
            }
        }
    }

    printProperties() {
        console.log(this.properties);
    }

    write(newFilepath, modifications) {
        if (modifications) {
            Object.assign(this.properties, modifications);
        }
        let buffer = Buffer.alloc(1024 * 1024); // Adjustable
        let offset = 0;

        // Write .RMF
        offset += buffer.write('.RMF', offset, 4, 'ascii');
        buffer.writeUInt32BE(18, offset); offset += 4;
        buffer.writeUInt16BE(0, offset); offset += 2;
        buffer.writeUInt32BE(this.properties.file_version || 0, offset); offset += 4;
        buffer.writeUInt32BE(this.properties.num_headers || 0, offset); offset += 4;

        // Write PROP
        offset += buffer.write('PROP', offset, 4, 'ascii');
        buffer.writeUInt32BE(50, offset); offset += 4;
        buffer.writeUInt16BE(0, offset); offset += 2;
        buffer.writeUInt32BE(this.properties.max_bit_rate || 0, offset); offset += 4;
        buffer.writeUInt32BE(this.properties.avg_bit_rate || 0, offset); offset += 4;
        buffer.writeUInt32BE(this.properties.max_packet_size || 0, offset); offset += 4;
        buffer.writeUInt32BE(this.properties.avg_packet_size || 0, offset); offset += 4;
        buffer.writeUInt32BE(this.properties.num_packets || 0, offset); offset += 4;
        buffer.writeUInt32BE(this.properties.duration || 0, offset); offset += 4;
        buffer.writeUInt32BE(this.properties.preroll || 0, offset); offset += 4;
        buffer.writeUInt32BE(this.properties.index_offset || 0, offset); offset += 4;
        buffer.writeUInt32BE(this.properties.data_offset || 0, offset); offset += 4;
        buffer.writeUInt16BE(this.properties.num_streams || 0, offset); offset += 2;
        buffer.writeUInt16BE(this.properties.flags || 0, offset); offset += 2;

        // Write CONT if present
        if (this.properties.title) {
            const title = Buffer.from(this.properties.title);
            const author = Buffer.from(this.properties.author);
            const copyright = Buffer.from(this.properties.copyright);
            const comment = Buffer.from(this.properties.comment);
            const contSize = 10 + 8 + title.length + author.length + copyright.length + comment.length;
            offset += buffer.write('CONT', offset, 4, 'ascii');
            buffer.writeUInt32BE(contSize, offset); offset += 4;
            buffer.writeUInt16BE(0, offset); offset += 2;
            buffer.writeUInt16BE(title.length, offset); offset += 2;
            title.copy(buffer, offset); offset += title.length;
            buffer.writeUInt16BE(author.length, offset); offset += 2;
            author.copy(buffer, offset); offset += author.length;
            buffer.writeUInt16BE(copyright.length, offset); offset += 2;
            copyright.copy(buffer, offset); offset += copyright.length;
            buffer.writeUInt16BE(comment.length, offset); offset += 2;
            comment.copy(buffer, offset); offset += comment.length;
        }

        // Write MDPR
        for (const key in this.properties) {
            if (key.startsWith('stream_')) {
                const stream = this.properties[key];
                const name = Buffer.from(stream.stream_name);
                const mime = Buffer.from(stream.mime_type);
                const tsData = Buffer.from(stream.type_specific_data, 'hex');
                const mdprSize = 10 + 38 + 1 + name.length + 1 + mime.length + 4 + tsData.length;
                offset += buffer.write('MDPR', offset, 4, 'ascii');
                buffer.writeUInt32BE(mdprSize, offset); offset += 4;
                buffer.writeUInt16BE(0, offset); offset += 2;
                const streamNum = parseInt(key.split('_')[1]);
                buffer.writeUInt16BE(streamNum, offset); offset += 2;
                buffer.writeUInt32BE(stream.max_bit_rate, offset); offset += 4;
                buffer.writeUInt32BE(stream.avg_bit_rate, offset); offset += 4;
                buffer.writeUInt32BE(stream.max_packet_size, offset); offset += 4;
                buffer.writeUInt32BE(stream.avg_packet_size, offset); offset += 4;
                buffer.writeUInt32BE(stream.start_time, offset); offset += 4;
                buffer.writeUInt32BE(stream.preroll, offset); offset += 4;
                buffer.writeUInt32BE(stream.duration, offset); offset += 4;
                buffer.writeUInt8(name.length, offset); offset += 1;
                name.copy(buffer, offset); offset += name.length;
                buffer.writeUInt8(mime.length, offset); offset += 1;
                mime.copy(buffer, offset); offset += mime.length;
                buffer.writeUInt32BE(tsData.length, offset); offset += 4;
                tsData.copy(buffer, offset); offset += tsData.length;
            }
        }

        // Write other chunks
        for (const chunk of this.otherChunks) {
            chunk.copy(buffer, offset);
            offset += chunk.length;
        }

        // Write data chunks
        for (const chunk of this.dataChunks) {
            chunk.copy(buffer, offset);
            offset += chunk.length;
        }

        fs.writeFileSync(newFilepath, buffer.slice(0, offset));
    }
}

// Example usage
// const rmvb = new RMVBFile('sample.rmvb');
// rmvb.decode();
// rmvb.printProperties();
// rmvb.write('modified.rmvb', { duration: 100000 });

7. C++ Class for .RMVB File Handling

This C++ class uses fstream and manual big-endian reading. It reads, decodes, prints to console, and writes modified files. Compile with g++.

#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <iomanip>
#include <cstring>
#include <sstream>

class RMVBFile {
private:
    std::string filepath;
    std::map<std::string, std::string> properties; // Simplified to string for print
    std::vector<std::vector<char>> dataChunks;
    std::vector<std::vector<char>> otherChunks;

    uint32_t readDWord(std::ifstream& f) {
        uint32_t val;
        f.read(reinterpret_cast<char*>(&val), 4);
        return __builtin_bswap32(val); // Big-endian
    }

    uint16_t readWord(std::ifstream& f) {
        uint16_t val;
        f.read(reinterpret_cast<char*>(&val), 2);
        return __builtin_bswap16(val);
    }

    uint8_t readByte(std::ifstream& f) {
        uint8_t val;
        f.read(reinterpret_cast<char*>(&val), 1);
        return val;
    }

    std::string readString(std::ifstream& f, int len) {
        std::vector<char> buf(len);
        f.read(buf.data(), len);
        return std::string(buf.begin(), buf.end());
    }

    std::string readBinary(std::ifstream& f, int len) {
        std::vector<char> buf(len);
        f.read(buf.data(), len);
        std::stringstream ss;
        for (char c : buf) ss << std::hex << std::setw(2) << std::setfill('0') << (int)(unsigned char)c << " ";
        return ss.str();
    }

public:
    RMVBFile(const std::string& filepath) : filepath(filepath) {}

    void decode() {
        std::ifstream f(filepath, std::ios::binary);
        if (!f) return;

        while (f) {
            char type[5] = {0};
            f.read(type, 4);
            if (f.eof()) break;
            uint32_t size = readDWord(f);
            uint16_t version = readWord(f);
            if (std::strcmp(type, ".RMF") == 0) {
                properties["file_version"] = std::to_string(readDWord(f));
                properties["num_headers"] = std::to_string(readDWord(f));
            } else if (std::strcmp(type, "PROP") == 0) {
                properties["max_bit_rate"] = std::to_string(readDWord(f));
                properties["avg_bit_rate"] = std::to_string(readDWord(f));
                properties["max_packet_size"] = std::to_string(readDWord(f));
                properties["avg_packet_size"] = std::to_string(readDWord(f));
                properties["num_packets"] = std::to_string(readDWord(f));
                properties["duration"] = std::to_string(readDWord(f));
                properties["preroll"] = std::to_string(readDWord(f));
                properties["index_offset"] = std::to_string(readDWord(f));
                properties["data_offset"] = std::to_string(readDWord(f));
                properties["num_streams"] = std::to_string(readWord(f));
                uint16_t flags = readWord(f);
                properties["flags"] = std::to_string(flags);
                properties["save_allowed"] = std::to_string((flags & 1) != 0);
                properties["perfect_play"] = std::to_string((flags & 2) != 0);
                properties["live_broadcast"] = std::to_string((flags & 4) != 0);
            } else if (std::strcmp(type, "CONT") == 0) {
                properties["title"] = readString(f, readWord(f));
                properties["author"] = readString(f, readWord(f));
                properties["copyright"] = readString(f, readWord(f));
                properties["comment"] = readString(f, readWord(f));
            } else if (std::strcmp(type, "MDPR") == 0) {
                uint16_t streamNum = readWord(f);
                std::string streamKey = "stream_" + std::to_string(streamNum) + "_";
                properties[streamKey + "max_bit_rate"] = std::to_string(readDWord(f));
                properties[streamKey + "avg_bit_rate"] = std::to_string(readDWord(f));
                properties[streamKey + "max_packet_size"] = std::to_string(readDWord(f));
                properties[streamKey + "avg_packet_size"] = std::to_string(readDWord(f));
                properties[streamKey + "start_time"] = std::to_string(readDWord(f));
                properties[streamKey + "preroll"] = std::to_string(readDWord(f));
                properties[streamKey + "duration"] = std::to_string(readDWord(f));
                properties[streamKey + "stream_name"] = readString(f, readByte(f));
                properties[streamKey + "mime_type"] = readString(f, readByte(f));
                uint32_t tsLen = readDWord(f);
                properties[streamKey + "type_specific_data"] = readBinary(f, tsLen);
            } else if (std::strcmp(type, "DATA") == 0) {
                std::streampos pos = f.tellg();
                f.seekg(pos - 10);
                std::vector<char> chunk(size);
                f.read(chunk.data(), size);
                dataChunks.push_back(chunk);
            } else {
                std::streampos pos = f.tellg();
                f.seekg(pos - 10);
                std::vector<char> chunk(size);
                f.read(chunk.data(), size);
                otherChunks.push_back(chunk);
            }
        }
    }

    void printProperties() {
        for (const auto& pair : properties) {
            std::cout << pair.first << ": " << pair.second << std::endl;
        }
    }

    void write(const std::string& newFilepath) {
        std::ofstream f(newFilepath, std::ios::binary);
        if (!f) return;

        // Write .RMF
        f.write(".RMF", 4);
        uint32_t sz = __builtin_bswap32(18);
        f.write(reinterpret_cast<char*>(&sz), 4);
        uint16_t ver = __builtin_bswap16(0);
        f.write(reinterpret_cast<char*>(&ver), 2);
        uint32_t fv = __builtin_bswap32(std::stoul(properties["file_version"]));
        f.write(reinterpret_cast<char*>(&fv), 4);
        uint32_t nh = __builtin_bswap32(std::stoul(properties["num_headers"]));
        f.write(reinterpret_cast<char*>(&nh), 4);

        // Write PROP
        f.write("PROP", 4);
        sz = __builtin_bswap32(50);
        f.write(reinterpret_cast<char*>(&sz), 4);
        ver = __builtin_bswap16(0);
        f.write(reinterpret_cast<char*>(&ver), 2);
        uint32_t mbr = __builtin_bswap32(std::stoul(properties["max_bit_rate"]));
        f.write(reinterpret_cast<char*>(&mbr), 4);
        uint32_t abr = __builtin_bswap32(std::stoul(properties["avg_bit_rate"]));
        f.write(reinterpret_cast<char*>(&abr), 4);
        uint32_t mps = __builtin_bswap32(std::stoul(properties["max_packet_size"]));
        f.write(reinterpret_cast<char*>(&mps), 4);
        uint32_t aps = __builtin_bswap32(std::stoul(properties["avg_packet_size"]));
        f.write(reinterpret_cast<char*>(&aps), 4);
        uint32_t np = __builtin_bswap32(std::stoul(properties["num_packets"]));
        f.write(reinterpret_cast<char*>(&np), 4);
        uint32_t dur = __builtin_bswap32(std::stoul(properties["duration"]));
        f.write(reinterpret_cast<char*>(&dur), 4);
        uint32_t pr = __builtin_bswap32(std::stoul(properties["preroll"]));
        f.write(reinterpret_cast<char*>(&pr), 4);
        uint32_t io = __builtin_bswap32(std::stoul(properties["index_offset"]));
        f.write(reinterpret_cast<char*>(&io), 4);
        uint32_t doff = __builtin_bswap32(std::stoul(properties["data_offset"]));
        f.write(reinterpret_cast<char*>(&doff), 4);
        uint16_t ns = __builtin_bswap16(std::stoul(properties["num_streams"]));
        f.write(reinterpret_cast<char*>(&ns), 2);
        uint16_t fl = __builtin_bswap16(std::stoul(properties["flags"]));
        f.write(reinterpret_cast<char*>(&fl), 2);

        // Write CONT if present
        if (properties.count("title")) {
            std::string title = properties["title"];
            std::string author = properties["author"];
            std::string copyright = properties["copyright"];
            std::string comment = properties["comment"];
            uint32_t contSize = 10 + 8 + title.size() + author.size() + copyright.size() + comment.size();
            f.write("CONT", 4);
            contSize = __builtin_bswap32(contSize);
            f.write(reinterpret_cast<char*>(&contSize), 4);
            ver = __builtin_bswap16(0);
            f.write(reinterpret_cast<char*>(&ver), 2);
            uint16_t tl = __builtin_bswap16(title.size());
            f.write(reinterpret_cast<char*>(&tl), 2);
            f.write(title.c_str(), title.size());
            uint16_t al = __builtin_bswap16(author.size());
            f.write(reinterpret_cast<char*>(&al), 2);
            f.write(author.c_str(), author.size());
            uint16_t cl = __builtin_bswap16(copyright.size());
            f.write(reinterpret_cast<char*>(&cl), 2);
            f.write(copyright.c_str(), copyright.size());
            uint16_t cm = __builtin_bswap16(comment.size());
            f.write(reinterpret_cast<char*>(&cm), 2);
            f.write(comment.c_str(), comment.size());
        }

        // Write MDPR (scan for streams)
        for (auto it = properties.begin(); it != properties.end(); ++it) {
            if (it->first.find("stream_") == 0 && it->first.find("max_bit_rate") != std::string::npos) {
                size_t pos = it->first.find('_');
                size_t pos2 = it->first.find('_', pos + 1);
                std::string streamNumStr = it->first.substr(pos + 1, pos2 - pos - 1);
                std::string streamKey = "stream_" + streamNumStr + "_";
                std::string name = properties[streamKey + "stream_name"];
                std::string mime = properties[streamKey + "mime_type"];
                std::string tsHex = properties[streamKey + "type_specific_data"];
                std::vector<char> tsData;
                std::stringstream ss(tsHex);
                std::string byteStr;
                while (ss >> byteStr) {
                    tsData.push_back(static_cast<char>(std::stoi(byteStr, nullptr, 16)));
                }
                uint32_t mdprSize = 10 + 38 + 1 + name.size() + 1 + mime.size() + 4 + tsData.size();
                f.write("MDPR", 4);
                mdprSize = __builtin_bswap32(mdprSize);
                f.write(reinterpret_cast<char*>(&mdprSize), 4);
                ver = __builtin_bswap16(0);
                f.write(reinterpret_cast<char*>(&ver), 2);
                uint16_t sn = __builtin_bswap16(std::stoi(streamNumStr));
                f.write(reinterpret_cast<char*>(&sn), 2);
                uint32_t mbr = __builtin_bswap32(std::stoi(properties[streamKey + "max_bit_rate"]));
                f.write(reinterpret_cast<char*>(&mbr), 4);
                uint32_t abr = __builtin_bswap32(std::stoi(properties[streamKey + "avg_bit_rate"]));
                f.write(reinterpret_cast<char*>(&abr), 4);
                uint32_t mps = __builtin_bswap32(std::stoi(properties[streamKey + "max_packet_size"]));
                f.write(reinterpret_cast<char*>(&mps), 4);
                uint32_t aps = __builtin_bswap32(std::stoi(properties[streamKey + "avg_packet_size"]));
                f.write(reinterpret_cast<char*>(&aps), 4);
                uint32_t st = __builtin_bswap32(std::stoi(properties[streamKey + "start_time"]));
                f.write(reinterpret_cast<char*>(&st), 4);
                uint32_t pr = __builtin_bswap32(std::stoi(properties[streamKey + "preroll"]));
                f.write(reinterpret_cast<char*>(&pr), 4);
                uint32_t dur = __builtin_bswap32(std::stoi(properties[streamKey + "duration"]));
                f.write(reinterpret_cast<char*>(&dur), 4);
                uint8_t nl = name.size();
                f.write(reinterpret_cast<char*>(&nl), 1);
                f.write(name.c_str(), name.size());
                uint8_t ml = mime.size();
                f.write(reinterpret_cast<char*>(&ml), 1);
                f.write(mime.c_str(), mime.size());
                uint32_t tsl = __builtin_bswap32(tsData.size());
                f.write(reinterpret_cast<char*>(&tsl), 4);
                f.write(tsData.data(), tsData.size());
            }
        }

        // Write other chunks
        for (const auto& chunk : otherChunks) {
            f.write(chunk.data(), chunk.size());
        }

        // Write data chunks
        for (const auto& chunk : dataChunks) {
            f.write(chunk.data(), chunk.size());
        }
    }
};

// Example usage
// int main() {
//     RMVBFile rmvb("sample.rmvb");
//     rmvb.decode();
//     rmvb.printProperties();
//     rmvb.properties["duration"] = "100000";
//     rmvb.write("modified.rmvb");
//     return 0;
// }