Skip to main content

TS to MP4 Converter

A Rust library that converts MPEG-2 TS (Transport Stream) files to MP4 format. Can be compiled to WebAssembly for use in web browsers.

Before You Start

This project started as a personal implementation to understand TS (Transport Stream) and MP4 formats, and for this reason, the content may be somewhat rough around the edges.

Features

  • H.264 video + AAC audio full support
  • Thumbnail extraction from TS and MP4 files
  • Pure Rust implementation (no SharedArrayBuffer required)
  • WebAssembly support
  • Safe for web environments with single-threaded operation
  • Zero-copy optimization
  • Compatible with major players like QuickTime, VLC, ffplay

Usage

As a Rust Library

If you wanna specific version, add to your Cargo.toml:

[dependencies]
ts2mp4 = "0.1.0"

then...

use ts2mp4::{convert_ts_to_mp4, extract_thumbnail_from_ts, extract_thumbnail_from_mp4};
use std::fs;

fn main() -> std::io::Result<()> {
    // Convert TS to MP4
    let ts_data = fs::read("input.ts")?;
    let mp4_data = convert_ts_to_mp4(&ts_data)?;
    fs::write("output.mp4", mp4_data)?;

    // Extract thumbnail from TS
    let thumbnail_data = extract_thumbnail_from_ts(&ts_data)?;
    fs::write("thumbnail_ts.h264", thumbnail_data)?;

    // Extract thumbnail from MP4
    let mp4_data = fs::read("input.mp4")?;
    let thumbnail_data = extract_thumbnail_from_mp4(&mp4_data)?;
    fs::write("thumbnail_mp4.h264", thumbnail_data)?;

    Ok(())
}

As a Python Library

pip install ts2mp4

Every function takes bytes and returns bytes. No temporary files and no external binary on PATH. Invalid input raises ValueError.

import ts2mp4

# MPEG-TS to MP4
mp4 = ts2mp4.convert_ts_to_mp4(ts_bytes)
mp4 = ts2mp4.convert_ts_to_mp4(ts_bytes, reset_timestamps=True)

# Fragmented MP4 to regular MP4
mp4 = ts2mp4.defragment_mp4(fmp4_bytes)
mp4 = ts2mp4.convert_mp4_reset_timestamps(mp4_bytes)

# HLS delivering picture and sound separately: join them into one file
mp4 = ts2mp4.mux_fmp4_tracks(video_bytes, audio_bytes)

# First keyframe as H.264
frame = ts2mp4.extract_thumbnail_from_mp4(mp4_bytes)

# Streaming segments
processor = ts2mp4.FragmentedMP4Processor()
processor.set_init_segment(init_bytes)
for segment in segments:
    processed = processor.process_segment(segment)
print(processor.base_decode_time)

Each argument is copied and the GIL is released for the duration of the work, so other threads keep running while a file is being processed.

Wheels are abi3, so one wheel per platform covers Python 3.9 and newer. Type stubs ship with the package.

As a CLI Tool

cargo install ts2mp4

# Convert TS to MP4
cargo run --release -- convert input.ts output.mp4

# Extract thumbnail from TS file
cargo run --release -- thumbnail-ts input.ts thumbnail.h264

# Extract thumbnail from MP4 file
cargo run --release -- thumbnail-mp4 input.mp4 thumbnail.h264

The thumbnail is extracted as a raw H.264 keyframe (I-frame) which can be:

  • Converted to an image using ffmpeg: ffmpeg -i thumbnail.h264 -frames:v 1 thumbnail.jpg
  • Used directly in video processing applications
  • Decoded by H.264 decoders

For developer

# dev
cargo build

# release
cargo build --release

# then
./target/release/ts2mp4 ${INPUT_TS} ${OUTPUT_MP4}

For WebAssembly

Build from source:

# Install wasm-pack
cargo install wasm-pack

# Build
wasm-pack build --target web

This will generate the following files in the pkg/ directory:

  • ts2mp4.js - JavaScript bindings
  • ts2mp4_bg.wasm - WebAssembly binary
  • ts2mp4.d.ts - TypeScript type definitions

Using in Web Browsers

DEMO Page

Examples:

import init, {
  convert_ts_to_mp4_wasm,
  extract_thumbnail_from_ts_wasm,
  extract_thumbnail_from_mp4_wasm
} from './pkg/ts2mp4.js';

// Initialize WASM
await init();

// Convert TS to MP4
const tsData = new Uint8Array(await file.arrayBuffer());
const mp4Data = convert_ts_to_mp4_wasm(tsData);

// Extract thumbnail from TS
const thumbnailFromTs = extract_thumbnail_from_ts_wasm(tsData);

// Extract thumbnail from MP4
const mp4Data = new Uint8Array(await mp4File.arrayBuffer());
const thumbnailFromMp4 = extract_thumbnail_from_mp4_wasm(mp4Data);

Why Not Use SharedArrayBuffer?

This library is intentionally designed not to use SharedArrayBuffer:

Advantages

  1. Browser Compatibility: SharedArrayBuffer requires COOP/COEP header configuration, making it difficult to use in many hosting environments
  2. Security: Restricted in many browsers to mitigate Spectre vulnerabilities
  3. Simplicity: Can be used immediately without complex server configuration
  4. Single-threaded: Simple and predictable memory management

Performance Optimization Techniques

Good performance can be achieved without SharedArrayBuffer:

  1. Streaming Processing: Process in chunks instead of loading entire file into memory
  2. Web Workers: Execute in workers to prevent main thread blocking
  3. Asynchronous Processing: Split work for large files
// Example using Web Worker
// worker.js
import init, { convert_ts_to_mp4_wasm } from './pkg/ts2mp4.js';

self.onmessage = async (e) => {
    await init();

    try {
        const mp4Data = convert_ts_to_mp4_wasm(e.data);
        self.postMessage({ success: true, data: mp4Data });
    } catch (error) {
        self.postMessage({ success: false, error: error.message });
    }
};

// main.js
const worker = new Worker('worker.js', { type: 'module' });

worker.onmessage = (e) => {
    if (e.data.success) {
        // Process MP4 data
        const blob = new Blob([e.data.data], { type: 'video/mp4' });
        // ...
    } else {
        console.error('Conversion failed:', e.data.error);
    }
};

// Start conversion
worker.postMessage(tsData);

Supported Features

Currently Supported

  • Video Codec: H.264 (AVC) Main/High Profile
  • Audio Codec: AAC-LC (Stereo, 48kHz)
  • Container: MP4 (ISO/IEC 14496-12 compliant)
  • Metadata: Complete moov/trak/stbl structure
  • Timestamps: Accurate synchronization based on PTS/DTS
  • Auto-detection:
    • Resolution (SPS parsing)
    • Frame rate

Future Plans

  • Additional audio codecs (Opus, etc.)
  • Multiple audio/subtitle tracks
  • Variable frame rate support
  • HDR metadata
  • Timestamp processing improvements

Development Guide

For detailed development and debugging instructions, see DEV_GUIDE.md.

Quick Start

# Build
cargo build --release

# Test
./target/release/ts2mp4 input.ts output.mp4

# Verify
ffprobe output.mp4
ffplay output.mp4

Debugging Tools

The test-scripts/ directory contains Python scripts for analyzing MP4 file structure:

  • test-scripts/analyze_mp4.py - Analyze box structure
  • test-scripts/check_all_durations.py - Verify durations
  • test-scripts/check_stts.py - Check Time-to-Sample
  • test-scripts/verify_audio_data.py - Verify audio data positions

For detailed usage, refer to DEV_GUIDE.md.

Troubleshooting

For common issues and solutions, refer to the "Troubleshooting" section in DEV_GUIDE.md.

Additional Resources

docs description lang
TS_STRUCTURE.md TS packet generation and processing ko, en
MP4_STRUCTURE.md MP4 generation and structure ko, en
90kHZ_MAGIC.md Why is 90kHz used for frame synchronization? ko, en

License

MIT

Contributing

Issues and PRs are always welcome!

Metadata

Release files for ts2mp4 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ts2mp4 0.4.0
File Size Uploaded
ts2mp4-0.4.0.tar.gz 175.7 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for ts2mp4 0.4.0
File
ts2mp4-0.4.0-cp39-abi3-win_amd64.whl CPython 3.9 abi3 Windows x86-64 Details
ts2mp4-0.4.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.9 abi3 Linux glibc 2.17+ x86-64 Details
ts2mp4-0.4.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.9 abi3 Linux glibc 2.17+ ARM64 Details
ts2mp4-0.4.0-cp39-abi3-macosx_11_0_arm64.whl CPython 3.9 abi3 macOS 11.0+ ARM64 Details
ts2mp4-0.4.0-cp39-abi3-macosx_10_12_x86_64.whl CPython 3.9 abi3 macOS 10.12+ x86-64 Details

Total release size: 1.7 MB

Release files / ts2mp4-0.4.0.tar.gz

Download URL ts2mp4-0.4.0.tar.gz
Size 175.7 kB
Tags Source
SHA-256 checksum
How to use checksums
64d2d7a350ccfb31ab16982d6406b9a3a6f859ae0cf531df968af42347ba3028
BLAKE2b-256 checksum
How to use checksums
559af2d7f9ec27c6c1f60958a8d743aa6917674084110dbc6876a97c93754e57
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 11, 2026.

Transparency log

Release files / ts2mp4-0.4.0-cp39-abi3-win_amd64.whl

Download URL ts2mp4-0.4.0-cp39-abi3-win_amd64.whl
Size 204.1 kB
Tags CPython 3.9 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
d8e4edfa6e0afba40ada091072284fd661e676ace141a862ba60aeb6671d6954
BLAKE2b-256 checksum
How to use checksums
76492f78f300c0c1f1b7ada306014e88637f253f289110bc84056cc5905846e7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 11, 2026.

Transparency log

Release files / ts2mp4-0.4.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL ts2mp4-0.4.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 346.3 kB
Tags CPython 3.9 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
90b35e316c34574795f8ec579a173a3e9e60e1eebbe3cdc9a8b9f82fcea95bec
BLAKE2b-256 checksum
How to use checksums
615012002c2347c6b83fa99a5d3e20ea6b0c7799bb3d8e1dbc7b8fefc733254f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 11, 2026.

Transparency log

Release files / ts2mp4-0.4.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL ts2mp4-0.4.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 338.8 kB
Tags CPython 3.9 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
56b01c503d2ba0d0290eb1111b0b9216d24005dd918b3001ee2bbe1e4c8d5cd9
BLAKE2b-256 checksum
How to use checksums
75a67f83fa1510612391ae5bc38b9856d7ce96bbf7ac477f281040eb144170ba
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 11, 2026.

Transparency log

Release files / ts2mp4-0.4.0-cp39-abi3-macosx_11_0_arm64.whl

Download URL ts2mp4-0.4.0-cp39-abi3-macosx_11_0_arm64.whl
Size 306.0 kB
Tags CPython 3.9 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
866182be40a4ca3be8c0de5383fe1f7afe2ae44660e49efc58f583bae49df66f
BLAKE2b-256 checksum
How to use checksums
f1f7e670a72bd74d75175ef66c30635dafce58fd5fb7c5c000a3affb3c7c39a4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 11, 2026.

Transparency log

Release files / ts2mp4-0.4.0-cp39-abi3-macosx_10_12_x86_64.whl

Download URL ts2mp4-0.4.0-cp39-abi3-macosx_10_12_x86_64.whl
Size 305.2 kB
Tags CPython 3.9 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
121ff3165956ed580c57be0d13306ffe63ce1e0337306e14fde2b4995db9ef4f
BLAKE2b-256 checksum
How to use checksums
4beb0220dd7292a7e18f3eee1e0addf485b604a0ca761d8df4aa81087f30fa66
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 11, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.4.0 This release

6 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page