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 bindingsts2mp4_bg.wasm- WebAssembly binaryts2mp4.d.ts- TypeScript type definitions
Using in Web Browsers
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
- Browser Compatibility: SharedArrayBuffer requires COOP/COEP header configuration, making it difficult to use in many hosting environments
- Security: Restricted in many browsers to mitigate Spectre vulnerabilities
- Simplicity: Can be used immediately without complex server configuration
- Single-threaded: Simple and predictable memory management
Performance Optimization Techniques
Good performance can be achieved without SharedArrayBuffer:
- Streaming Processing: Process in chunks instead of loading entire file into memory
- Web Workers: Execute in workers to prevent main thread blocking
- 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 structuretest-scripts/check_all_durations.py- Verify durationstest-scripts/check_stts.py- Check Time-to-Sampletest-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)
| File | Size | Uploaded | |
|---|---|---|---|
| ts2mp4-0.4.0.tar.gz | 175.7 kB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| 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 logRelease 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 logRelease 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 logRelease 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 logRelease 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 logRelease 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