> [!NOTE]
> This project is a rewrite of the ErsatzTV streaming engine in Rust. It is the default streaming engine in [ErsatzTV (legacy)](https://github.com/ErsatzTV/legacy), and can also run standalone. Configuration and playout formats are versioned, and breaking changes are called out in the [changelog](CHANGELOG.md).
# ErsatzTV
ErsatzTV is a modular, self-hosted IPTV server that transcodes and streams your media as live TV channels.
## Background
This rewrite focuses on a "one thing well" philosophy: **reliable transcoding and streaming**.
> [!IMPORTANT]
> Library and metadata management, scheduling and playout creation **are not in scope for this project**.
Unlike [the legacy version](https://github.com/ErsatzTV/legacy), this version is decoupled from library management and scheduling. It consumes **playouts** (JSON documents describing what to play and when) and handles the heavy lifting of keeping a stream alive and consistent, regardless of source media variations.
### Ways to use it
- **With ErsatzTV (legacy):** channels use this engine by default (**Streaming Engine** set to **Next**). Legacy keeps handling libraries and scheduling, and writes the playout and channel configuration that this engine consumes. No separate install is needed.
- **Standalone:** bring your own scheduler (or hand-written playouts) that writes JSON matching the [schemas](schema), and run the `ersatztv` server described below.
## Features
- **Hardware acceleration:** AMF, CUDA, QSV, RKMPP, VAAPI, VideoToolbox and Vulkan. Capabilities are probed at runtime, and anything the hardware can't do falls back to software.
- **Normalization:** every item is transcoded to the channel's configured format, resolution and audio layout, including HDR10 and Dolby Vision tonemapping, deinterlacing, and anamorphic and rotated sources.
- **Stream copy:** channels can copy video and/or audio instead of transcoding when the source already matches.
- **Overlays:** watermarks, graphics layers and burned-in subtitles.
- **Sources:** local files, HTTP and RTSP streams, commands that write MPEG-TS to stdout, and dynamic items that are resolved over HTTP at playback time.
- **Gap filling:** schedule gaps, missing playouts and items that fail to transcode are replaced with a fallback stream instead of dropping the channel.
- **IPTV output:** HLS per channel, an M3U channel list, and XMLTV guide data.
## Documentation
Quickstart guides and reference documentation live at ****.
## Contents
This project contains the following crates:
- [ffpipeline](crates/ffpipeline): transcoding and normalization logic
- [ersatztv-playout](crates/ersatztv-playout): Rust models for the playout JSON schema
- [ersatztv-channel](crates/ersatztv-channel): generates a normalized IPTV stream for a single channel from playout JSON
- [ersatztv](crates/ersatztv): serves IPTV over HTTP (M3U, M3U8, XMLTV) and manages channel processes
- [ersatztv-core](crates/ersatztv-core): shared utilities, including config merging and schema versioning
- [ersatztv-playout-generator](crates/ersatztv-playout-generator): generates playout JSON from a folder of video files. *Provided for demonstration and reference purposes; scheduling is not in scope and feature requests will not be accepted.*
- `lib*-sys` (e.g. [libva-sys](crates/libva-sys), [libvpl-sys](crates/libvpl-sys)): FFI bindings used to probe hardware acceleration capabilities
The JSON schemas under [schema](schema) are the public contract for integrators: [playout.json](schema/playout.json), [channel_config.json](schema/channel_config.json) and [lineup_config.json](schema/lineup_config.json). Each document carries a `version`; files with an incompatible version are rejected with an error rather than misread.
Finally, there are configuration examples under [examples](examples):
- [playout.json](examples/playout/playout.json): an example playout JSON file, demonstrating some of the possible fields.
- [channel.json](examples/channel.json): an example channel configuration, linking a channel to its playout JSON files, and describing how to normalize the content.
- [channel_copy.json](examples/channel_copy.json): an example channel configuration that copies video and audio when the source format allows it.
- [lineup.json](examples/lineup.json): an example lineup configuration, linking to all channels, and describing where to write the normalized content and how to serve it over HTTP.
## Getting Started
### Prerequisites
- **ErsatzTV's ffmpeg build.** Use `ffmpeg` and `ffprobe` from [ErsatzTV-ffmpeg](https://github.com/ErsatzTV/ErsatzTV-ffmpeg/releases/latest), either in your `PATH` or referenced by absolute path in `channel.json`. It carries patches and fixes that this project relies on; stock or distro ffmpeg builds are not supported. The Docker image already includes it.
### Install
- **Docker:** `ersatztv/next:latest` (also `ghcr.io/ersatztv/next:latest`) for `linux/amd64` and `linux/arm64`. Use a release-line tag such as `:v0.2` to get fixes and features without breaking changes.
- **Binaries:** download a build for Windows, Linux (x64, x64 musl, arm64) or macOS (x64, arm64) from the [latest release](https://github.com/ErsatzTV/next/releases/latest).
- **Development builds:** binaries from `main` are in [ErsatzTV/next-develop-builds](https://github.com/ErsatzTV/next-develop-builds/releases/latest), and images are tagged `:develop`.
- **Source:** `cargo build --release --workspace`.
Releases use semantic versioning; before 1.0, `0.B.C` bumps `B` for breaking changes. Each release's notes list the config versions it reads and the ErsatzTV-ffmpeg build it was tested with. See [Releases](https://ersatztv.org/next-docs/releases) for the details and every Docker tag.
### Quick Start
1. **Scaffold a lineup with one channel:**
```bash
ersatztv add-lineup config/lineup.json --channels 1
```
This creates `config/lineup.json`, `config/hls/`, `config/channels/1/channel.json`, and `config/channels/1/playout/`.
2. **Generate a test playout** from a folder of video files:
```bash
ersatztv-playout-generator --lineup config/lineup.json --channel 1 --content-folder /path/to/videos
```
3. **Run the server:**
```bash
ersatztv config/lineup.json
```
4. **Watch** at `http://localhost:8409/channel/1.m3u8` in VLC, mpv, or any HLS player. For a no-install check, open the [hls.js demo](https://hlsjs.video-dev.org/demo/?src=http%3A%2F%2Flocalhost%3A8409%2Fchannel%2F1.m3u8).
IPTV clients can use the channel list at `http://localhost:8409/channels.m3u` and guide data at `http://localhost:8409/xmltv.xml`.
### Quick Start (Docker)
The image runs `ersatztv /config/lineup.json`. Media paths in your playouts must be valid inside the container, so mount your media at the same path you use in the playouts:
```bash
# scaffold
docker run --rm -v ./config:/config ersatztv/next:latest add-lineup /config/lineup.json --channels 1
# generate a test playout
docker run --rm -v ./config:/config -v /path/to/videos:/path/to/videos \
--entrypoint /app/ersatztv-playout-generator ersatztv/next:latest \
--lineup /config/lineup.json --channel 1 --content-folder /path/to/videos
# run the server
docker run -d -p 8409:8409 -v ./config:/config -v /path/to/videos:/path/to/videos ersatztv/next:latest
```
For hardware acceleration, pass the device through: `--device /dev/dri` for VAAPI/QSV, or `--gpus all` for NVIDIA.
## Reporting Issues
When a stream fails, the most useful report includes:
- the output of `ersatztv-channel debug `, which logs the merged channel config, the ffmpeg build features and the hardware capabilities this engine detected;
- an error dossier: set `ffmpeg.reports_folder` in `channel.json`, and each failed transcode writes a folder containing the ffmpeg report and stderr, the generated pipeline, the playout item, media info and the channel config.
## Contributing
We welcome feedback and contributions!
- **Matrix:** [#ersatztv-dev:matrix.org](https://matrix.to/#/#ersatztv-dev:matrix.org)
- **Discord:** [#developer-chat](https://discord.ersatztv.org)
Early feedback on the **playout schema** and architecture is especially valuable at this stage.
## License
ErsatzTV is licensed under the [MIT License](LICENSE).