Task 736: .TS File Format

Task 736: .TS File Format

File Format Specifications for .TS (MPEG-2 Transport Stream)

The .TS file format refers to the MPEG-2 Transport Stream (MPEG-TS), a standard digital container format defined in ISO/IEC 13818-1 (also known as ITU-T Rec. H.222.0). It is designed for the transmission and storage of audio, video, and associated data in broadcast systems, such as digital television (DVB, ATSC, IPTV). The format consists of fixed-length packets of 188 bytes, enabling error correction and multiplexing of multiple streams.

List of Properties Intrinsic to the File Format
The following properties define the core structure of the .TS format, focusing on the packet header and key elements. These are derived from the MPEG-2 specification and are essential for parsing and validating the file. Each packet in a .TS file adheres to this structure:

  • Packet Size: Fixed at 188 bytes per packet.
  • Sync Byte: 8 bits, always set to 0x47 (decimal 71) to mark the start of each packet.
  • Transport Error Indicator: 1 bit, indicates if the packet contains uncorrectable errors (1 if errors present, 0 otherwise).
  • Payload Unit Start Indicator: 1 bit, signals the beginning of a new payload unit, such as a Packetized Elementary Stream (PES) packet (1 if start, 0 otherwise).
  • Transport Priority: 1 bit, denotes higher priority for the packet in transmission (1 for high priority, 0 for normal).
  • PID (Packet Identifier): 13 bits, identifies the elementary stream or table to which the packet belongs (values range from 0 to 8191; e.g., 0 for PAT, null packets at 8191).
  • Transport Scrambling Control: 2 bits, specifies scrambling mode for the payload (00: not scrambled; 01/10/11: scrambled with even/odd/reserved keys).
  • Adaptation Field Control: 2 bits, indicates the presence of an adaptation field and/or payload (00: reserved; 01: adaptation field only; 10: payload only; 11: both).
  • Continuity Counter: 4 bits, increments modulo 16 for consecutive packets with the same PID to detect packet loss.
  • Adaptation Field Length (if Adaptation Field Control indicates presence): 8 bits, specifies the length of the adaptation field in bytes (0-183).
  • Adaptation Field Flags and Fields (optional, within adaptation field): Includes 1-bit flags for discontinuity indicator, random access indicator, elementary stream priority indicator, PCR flag, OPCR flag, splicing point flag, transport private data flag, and adaptation field extension flag; followed by corresponding fields like Program Clock Reference (PCR: 42 bits base + 9 bits extension for timing).

Additionally, the format includes Program-Specific Information (PSI) tables multiplexed in packets:

  • Program Association Table (PAT): Maps program numbers to PMT PIDs.
  • Program Map Table (PMT): Describes elementary streams (e.g., video/audio types and PIDs) for each program.
  • Conditional Access Table (CAT): Provides conditional access system information.
  • Network Information Table (NIT): Contains network details.

These properties ensure robustness in error-prone environments and support multi-program transport streams.

Two Direct Download Links for .TS Files

Ghost Blog Embedded HTML/JavaScript for Drag-and-Drop .TS File Analysis
The following is a self-contained HTML page with embedded JavaScript that allows users to drag and drop a .TS file. It parses the file, extracts the properties listed above for the first 10 packets (to avoid overwhelming output for large files), and displays them on the screen.

.TS File Properties Dumper

Drag and Drop .TS File to Dump Properties

Drop .TS file here

Python Class for .TS File Handling
The following Python class can open a .TS file, decode packets, read and print properties for the first 10 packets (to manage output), and write a simple sample .TS file with null packets.

import struct
import os

class TSFileHandler:
    PACKET_SIZE = 188

    def __init__(self, filepath):
        self.filepath = filepath
        self.packets = []

    def read_and_decode(self):
        if not os.path.exists(self.filepath):
            print(f"File {self.filepath} does not exist.")
            return
        with open(self.filepath, 'rb') as f:
            data = f.read()
        offset = 0
        packet_count = 0
        while offset + self.PACKET_SIZE <= len(data) and packet_count < 10:
            packet = data[offset:offset + self.PACKET_SIZE]
            if packet[0] != 0x47:
                print(f"Invalid sync byte at offset {offset}. Stopping.")
                break
            byte1, byte2, byte3 = struct.unpack('>BBB', packet[1:4])
            transport_error = (byte1 >> 7) & 1
            payload_start = (byte1 >> 6) & 1
            transport_priority = (byte1 >> 5) & 1
            pid = ((byte1 & 0x1F) << 8) | byte2
            scrambling = (byte3 >> 6) & 3
            adaptation_control = (byte3 >> 4) & 3
            continuity = byte3 & 0xF
            adaptation_length = 0
            if adaptation_control in (2, 3):
                adaptation_length = packet[4]
            print(f"Packet {packet_count + 1}:")
            print(f"- Packet Size: {self.PACKET_SIZE} bytes")
            print(f"- Sync Byte: 0x47")
            print(f"- Transport Error Indicator: {transport_error}")
            print(f"- Payload Unit Start Indicator: {payload_start}")
            print(f"- Transport Priority: {transport_priority}")
            print(f"- PID: {pid}")
            print(f"- Transport Scrambling Control: {scrambling}")
            print(f"- Adaptation Field Control: {adaptation_control}")
            print(f"- Continuity Counter: {continuity}")
            print(f"- Adaptation Field Length: {adaptation_length}")
            print()
            self.packets.append(packet)
            offset += self.PACKET_SIZE
            packet_count += 1

    def write_sample(self, output_path, num_packets=5):
        with open(output_path, 'wb') as f:
            for _ in range(num_packets):
                # Create a simple null packet (PID 8191, no adaptation, continuity 0)
                header = struct.pack('>B', 0x47) + struct.pack('>BBB', 0x1F, 0xFF, 0x10)
                payload = b'\xFF' * (self.PACKET_SIZE - 4)
                f.write(header + payload)
        print(f"Sample .TS file written to {output_path} with {num_packets} null packets.")

# Example usage:
# handler = TSFileHandler('sample.ts')
# handler.read_and_decode()
# handler.write_sample('output.ts')

Java Class for .TS File Handling
The following Java class can open a .TS file, decode packets, read and print properties for the first 10 packets, and write a simple sample .TS file with null packets.

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Paths;

public class TSFileHandler {
    private static final int PACKET_SIZE = 188;
    private String filepath;

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

    public void readAndDecode() throws IOException {
        byte[] data = Files.readAllBytes(Paths.get(filepath));
        ByteBuffer buffer = ByteBuffer.wrap(data);
        int offset = 0;
        int packetCount = 0;
        while (offset + PACKET_SIZE <= data.length && packetCount < 10) {
            if (buffer.get(offset) != 0x47) {
                System.out.println("Invalid sync byte at offset " + offset + ". Stopping.");
                break;
            }
            int byte1 = buffer.get(offset + 1) & 0xFF;
            int byte2 = buffer.get(offset + 2) & 0xFF;
            int byte3 = buffer.get(offset + 3) & 0xFF;
            int transportError = (byte1 >> 7) & 1;
            int payloadStart = (byte1 >> 6) & 1;
            int transportPriority = (byte1 >> 5) & 1;
            int pid = ((byte1 & 0x1F) << 8) | byte2;
            int scrambling = (byte3 >> 6) & 3;
            int adaptationControl = (byte3 >> 4) & 3;
            int continuity = byte3 & 0xF;
            int adaptationLength = 0;
            if (adaptationControl == 2 || adaptationControl == 3) {
                adaptationLength = buffer.get(offset + 4) & 0xFF;
            }
            System.out.println("Packet " + (packetCount + 1) + ":");
            System.out.println("- Packet Size: " + PACKET_SIZE + " bytes");
            System.out.println("- Sync Byte: 0x47");
            System.out.println("- Transport Error Indicator: " + transportError);
            System.out.println("- Payload Unit Start Indicator: " + payloadStart);
            System.out.println("- Transport Priority: " + transportPriority);
            System.out.println("- PID: " + pid);
            System.out.println("- Transport Scrambling Control: " + scrambling);
            System.out.println("- Adaptation Field Control: " + adaptationControl);
            System.out.println("- Continuity Counter: " + continuity);
            System.out.println("- Adaptation Field Length: " + adaptationLength);
            System.out.println();
            offset += PACKET_SIZE;
            packetCount++;
        }
    }

    public void writeSample(String outputPath, int numPackets) throws IOException {
        try (FileOutputStream fos = new FileOutputStream(outputPath)) {
            for (int i = 0; i < numPackets; i++) {
                // Null packet: sync 0x47, header for PID 8191, no adaptation, continuity 0
                byte[] header = new byte[] {0x47, (byte)0x1F, (byte)0xFF, (byte)0x10};
                fos.write(header);
                for (int j = 4; j < PACKET_SIZE; j++) {
                    fos.write(0xFF);
                }
            }
        }
        System.out.println("Sample .TS file written to " + outputPath + " with " + numPackets + " null packets.");
    }

    // Example usage:
    // public static void main(String[] args) throws IOException {
    //     TSFileHandler handler = new TSFileHandler("sample.ts");
    //     handler.readAndDecode();
    //     handler.writeSample("output.ts", 5);
    // }
}

JavaScript Class for .TS File Handling
The following JavaScript class (for Node.js) can open a .TS file, decode packets, read and print properties for the first 10 packets to the console, and write a simple sample .TS file with null packets.

const fs = require('fs');

class TSFileHandler {
    static PACKET_SIZE = 188;

    constructor(filepath) {
        this.filepath = filepath;
    }

    readAndDecode() {
        if (!fs.existsSync(this.filepath)) {
            console.log(`File ${this.filepath} does not exist.`);
            return;
        }
        const data = fs.readFileSync(this.filepath);
        let offset = 0;
        let packetCount = 0;
        while (offset + TSFileHandler.PACKET_SIZE <= data.length && packetCount < 10) {
            if (data[offset] !== 0x47) {
                console.log(`Invalid sync byte at offset ${offset}. Stopping.`);
                break;
            }
            const byte1 = data[offset + 1];
            const byte2 = data[offset + 2];
            const byte3 = data[offset + 3];
            const transportError = (byte1 >> 7) & 1;
            const payloadStart = (byte1 >> 6) & 1;
            const transportPriority = (byte1 >> 5) & 1;
            const pid = ((byte1 & 0x1F) << 8) | byte2;
            const scrambling = (byte3 >> 6) & 3;
            const adaptationControl = (byte3 >> 4) & 3;
            const continuity = byte3 & 0xF;
            let adaptationLength = 0;
            if (adaptationControl === 2 || adaptationControl === 3) {
                adaptationLength = data[offset + 4];
            }
            console.log(`Packet ${packetCount + 1}:`);
            console.log(`- Packet Size: ${TSFileHandler.PACKET_SIZE} bytes`);
            console.log(`- Sync Byte: 0x47`);
            console.log(`- Transport Error Indicator: ${transportError}`);
            console.log(`- Payload Unit Start Indicator: ${payloadStart}`);
            console.log(`- Transport Priority: ${transportPriority}`);
            console.log(`- PID: ${pid}`);
            console.log(`- Transport Scrambling Control: ${scrambling}`);
            console.log(`- Adaptation Field Control: ${adaptationControl}`);
            console.log(`- Continuity Counter: ${continuity}`);
            console.log(`- Adaptation Field Length: ${adaptationLength}`);
            console.log('');
            offset += TSFileHandler.PACKET_SIZE;
            packetCount++;
        }
    }

    writeSample(outputPath, numPackets = 5) {
        const buffer = Buffer.alloc(numPackets * TSFileHandler.PACKET_SIZE);
        for (let i = 0; i < numPackets; i++) {
            const packetOffset = i * TSFileHandler.PACKET_SIZE;
            buffer[packetOffset] = 0x47;
            buffer[packetOffset + 1] = 0x1F;
            buffer[packetOffset + 2] = 0xFF;
            buffer[packetOffset + 3] = 0x10;
            for (let j = 4; j < TSFileHandler.PACKET_SIZE; j++) {
                buffer[packetOffset + j] = 0xFF;
            }
        }
        fs.writeFileSync(outputPath, buffer);
        console.log(`Sample .TS file written to ${outputPath} with ${numPackets} null packets.`);
    }
}

// Example usage:
// const handler = new TSFileHandler('sample.ts');
// handler.readAndDecode();
// handler.writeSample('output.ts');

C++ Class for .TS File Handling
The following C++ class can open a .TS file, decode packets, read and print properties for the first 10 packets to the console, and write a simple sample .TS file with null packets.

#include <iostream>
#include <fstream>
#include <vector>
#include <cstdint>

class TSFileHandler {
private:
    static constexpr int PACKET_SIZE = 188;
    std::string filepath;

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

    void readAndDecode() {
        std::ifstream file(filepath, std::ios::binary);
        if (!file) {
            std::cout << "File " << filepath << " does not exist." << std::endl;
            return;
        }
        std::vector<uint8_t> data((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
        size_t offset = 0;
        int packetCount = 0;
        while (offset + PACKET_SIZE <= data.size() && packetCount < 10) {
            if (data[offset] != 0x47) {
                std::cout << "Invalid sync byte at offset " << offset << ". Stopping." << std::endl;
                break;
            }
            uint8_t byte1 = data[offset + 1];
            uint8_t byte2 = data[offset + 2];
            uint8_t byte3 = data[offset + 3];
            int transportError = (byte1 >> 7) & 1;
            int payloadStart = (byte1 >> 6) & 1;
            int transportPriority = (byte1 >> 5) & 1;
            int pid = ((byte1 & 0x1F) << 8) | byte2;
            int scrambling = (byte3 >> 6) & 3;
            int adaptationControl = (byte3 >> 4) & 3;
            int continuity = byte3 & 0xF;
            int adaptationLength = 0;
            if (adaptationControl == 2 || adaptationControl == 3) {
                adaptationLength = data[offset + 4];
            }
            std::cout << "Packet " << (packetCount + 1) << ":" << std::endl;
            std::cout << "- Packet Size: " << PACKET_SIZE << " bytes" << std::endl;
            std::cout << "- Sync Byte: 0x47" << std::endl;
            std::cout << "- Transport Error Indicator: " << transportError << std::endl;
            std::cout << "- Payload Unit Start Indicator: " << payloadStart << std::endl;
            std::cout << "- Transport Priority: " << transportPriority << std::endl;
            std::cout << "- PID: " << pid << std::endl;
            std::cout << "- Transport Scrambling Control: " << scrambling << std::endl;
            std::cout << "- Adaptation Field Control: " << adaptationControl << std::endl;
            std::cout << "- Continuity Counter: " << continuity << std::endl;
            std::cout << "- Adaptation Field Length: " << adaptationLength << std::endl;
            std::cout << std::endl;
            offset += PACKET_SIZE;
            packetCount++;
        }
    }

    void writeSample(const std::string& outputPath, int numPackets = 5) {
        std::ofstream file(outputPath, std::ios::binary);
        if (!file) {
            std::cout << "Cannot open output file " << outputPath << "." << std::endl;
            return;
        }
        for (int i = 0; i < numPackets; ++i) {
            file.put(0x47);
            file.put(0x1F);
            file.put(0xFF);
            file.put(0x10);
            for (int j = 4; j < PACKET_SIZE; ++j) {
                file.put(0xFF);
            }
        }
        std::cout << "Sample .TS file written to " << outputPath << " with " << numPackets << " null packets." << std::endl;
    }
};

// Example usage:
// int main() {
//     TSFileHandler handler("sample.ts");
//     handler.readAndDecode();
//     handler.writeSample("output.ts");
//     return 0;
// }