Open Source Cross-Platform Transcoding Service & SDK
Project description
GhostStream
Open-Source Video Transcoding Server
GhostStream is a hardware-accelerated video transcoding server with automatic GPU detection, adaptive bitrate streaming, and minimal configuration. It serves as the transcoding backend for GhostHub but can be used standalone. It is designed for local-machine and LAN use, not direct public internet exposure.
Quick Start
Pre-built Binaries
| Platform | Download | Run |
|---|---|---|
| Windows | GhostStream.exe | Double-click |
| Linux | GhostStream-Linux | chmod +x && ./GhostStream-Linux |
| macOS | GhostStream-macOS | chmod +x && ./GhostStream-macOS |
Requires FFmpeg. On startup, GhostStream launches the Textual dashboard by default and will show install instructions if FFmpeg is missing.
From Source
git clone https://github.com/BleedingXiko/GhostStream.git
cd GhostStream
git submodule update --init --recursive
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
.venv/bin/python -m ghoststream
That creates a local development virtualenv in .venv, installs the server dependencies, and starts the Specter-native GhostStream runtime with the TUI dashboard by default.
If you prefer to stay inside the activated shell, this is equivalent:
python -m ghoststream
Headless / Containers
Use --server-only when you want the API/HLS engine without the dashboard:
.venv/bin/python -m ghoststream --server-only
That mode is ideal for Docker, CI smoke tests, browser example serving, SSH sessions, and other environments where a terminal UI is not practical. GhostStream is intended to stay on your machine or trusted LAN even in headless mode.
SDK Installation
Python:
pip install ghoststream # SDK only (lightweight)
pip install ghoststream[server] # Full server runtime, including Specter prerequisites
JavaScript/TypeScript:
npm install ghoststream-sdk
Usage
Python SDK (recommended):
from ghoststream import GhostStreamClient, TranscodeStatus
client = GhostStreamClient(manual_server="localhost:8765")
# Synchronous (Flask/gevent compatible)
job = client.transcode(source="https://example.com/video.mp4", resolution="720p")
print(f"Stream URL: {job.stream_url}")
JavaScript/TypeScript:
import { GhostStreamClient } from 'ghoststream-sdk';
const client = new GhostStreamClient('localhost:8765');
const job = await client.transcode({ source: 'https://example.com/video.mp4', resolution: '720p' });
console.log(`Stream URL: ${job.streamUrl}`);
curl:
curl -X POST http://localhost:8765/api/transcode/start \
-H "Content-Type: application/json" \
-d '{"source": "https://example.com/video.mp4", "mode": "stream"}'
See the examples/ directory for additional usage examples.
Features
- HLS Streaming - Real-time transcoding with immediate playback
- Adaptive Bitrate (ABR) - Multiple quality variants for bandwidth adaptation
- Subtitle Muxing - Native WebVTT subtitle support in HLS streams
- HDR to SDR - Automatic tone mapping for HDR content
- Codec Support - H.264, H.265/HEVC, VP9, AV1
- Batch Processing - Queue multiple files with optional two-pass encoding
- Hardware Acceleration - NVIDIA NVENC, Intel QuickSync, AMD AMF, Apple VideoToolbox
- Automatic Fallback - Falls back to software encoding if hardware fails
Supported Hardware Encoders
| Platform | Encoder | Detection |
|---|---|---|
| NVIDIA | NVENC | Automatic via nvidia-smi |
| Intel | QuickSync | Automatic via VA-API |
| AMD | AMF/VCE | Automatic |
| Apple | VideoToolbox | Native macOS support |
| CPU | libx264/libx265 | Always available |
API Reference
Endpoints
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/transcode/start |
Start a transcoding job |
GET |
/api/transcode/{id}/status |
Get job status & progress |
POST |
/api/transcode/{id}/cancel |
Cancel a job |
DELETE |
/api/transcode/{id} |
Delete job & cleanup |
GET |
/api/health |
Health check |
GET |
/api/capabilities |
Hardware & codec info |
WS |
/ws/progress |
Real-time progress via WebSocket |
Start Transcode Request
{
"source": "https://example.com/video.mp4",
"mode": "stream", // "stream", "abr", or "batch"
"output": {
"resolution": "720p", // "4k", "1080p", "720p", "480p", "original"
"video_codec": "h264", // "h264", "h265", "vp9", "av1"
"audio_codec": "aac", // "aac", "opus", "copy"
"hw_accel": "auto" // "auto", "nvenc", "qsv", "software"
},
"start_time": 0, // Seek to position (seconds)
"subtitles": [ // Optional: Subtitle tracks to mux
{
"url": "https://example.com/subtitle.vtt",
"label": "English",
"language": "en",
"default": true
}
]
}
Response
{
"job_id": "abc-123",
"status": "processing",
"stream_url": "http://localhost:8765/stream/abc-123/master.m3u8?gst=eyJ...",
"control_token": "eyJ...",
"progress": 0
}
Use the returned control_token on protected job endpoints:
curl http://localhost:8765/api/transcode/abc-123/status \
-H "X-GhostStream-Control-Token: eyJ..."
Use the returned stream_url as-is in players and web clients. GhostStream embeds the stream token there and propagates it through HLS playlists and segment URLs.
The gst=... query parameter is that stream capability token. It authorizes playlist, segment, and download access for that job, so clients should preserve it exactly as returned.
Examples
| File | Description |
|---|---|
demo.py |
Basic demo with auto-play |
demo.html |
Browser-based demo |
minimal.py |
Minimal Python example |
quickstart.py |
Interactive examples |
curl_examples.md |
HTTP/curl commands |
web_player.html |
Full-featured web player |
Running HTML Examples
The HTML examples must be served over HTTP due to browser CORS restrictions:
# 1. Start GhostStream
.venv/bin/python -m ghoststream --server-only
# 2. In another terminal, serve the examples
cd examples
.venv/bin/python -m http.server 8080
# 3. Open in browser
# http://localhost:8080/demo.html
# http://localhost:8080/web_player.html
If your virtualenv is already activated, plain python -m ghoststream --server-only and python -m http.server 8080 are equivalent.
Configuration
Create ghoststream.yaml to customize (optional):
server:
host: 0.0.0.0 # Bind on all local interfaces; use only on trusted networks
port: 8765
transcoding:
max_concurrent_jobs: 2
segment_duration: 4
tone_map_hdr: true
retry_count: 3
hardware:
prefer_hw_accel: true
fallback_to_software: true
GhostHub Integration
GhostStream serves as the transcoding backend for GhostHub.
Architecture
┌─────────────────────────────────┐ ┌─────────────────────────────────┐
│ Raspberry Pi │ │ Your PC │
│ ┌───────────────────────────┐ │ │ ┌───────────────────────────┐ │
│ │ GhostHub │ │ WiFi │ │ GhostStream │ │
│ │ (Media Server) │◄─┼──────┼─►│ (GPU Transcoder) │ │
│ └───────────────────────────┘ │ │ └───────────────────────────┘ │
└─────────────────────────────────┘ └─────────────────────────────────┘
- Auto-Discovery: GhostStream advertises via mDNS (
_ghoststream._tcp.local) - On-Demand: Transcoding occurs only when requested
- Local Network: No internet connection required
Python SDK
pip install ghoststream
from ghoststream import GhostStreamClient, TranscodeStatus
# Auto-discover on network
client = GhostStreamClient()
client.start_discovery()
# Or connect directly
client = GhostStreamClient(manual_server="192.168.1.100:8765")
# Synchronous API (Flask/gevent compatible)
job = client.transcode(
source="http://pi:5000/media/video.mkv",
resolution="1080p"
)
if job.status != TranscodeStatus.ERROR:
print(job.stream_url)
Progress Updates
# Python SDK: poll synchronously
job = client.get_job_status("job-123")
print(job.progress, job.status.value)
// Browser / JS clients can use /ws/progress directly
{"type": "subscribe", "job_ids": ["job-123"], "control_token": "token-from-start-response"}
{"type": "progress", "job_id": "job-123", "data": {"progress": 45.2}}
{"type": "status_change", "job_id": "job-123", "data": {"status": "ready"}}
For multi-job filtered subscriptions, send a job_tokens object keyed by job ID instead of a single control_token.
Contributing
After cloning, initialize the Specter submodule before running the server or tests:
git submodule update --init --recursive
The ghoststream/specter directory is sourced from BleedingXiko/SPECTER. If you need to make changes inside the submodule:
- Commit and push from within
ghoststream/specter. - Return to the GhostStream repo and commit the updated submodule pointer.
That keeps GhostStream pinned to an explicit Specter revision while still letting contributors iterate on both projects.
Contributions are welcome. See CONTRIBUTING.md for guidelines.
# Development setup
git clone https://github.com/BleedingXiko/GhostStream.git
cd GhostStream
python -m venv .venv && source .venv/bin/activate # or .venv\Scripts\activate on Windows
pip install -r requirements.txt
.venv/bin/python -m ghoststream --log-level DEBUG
License
MIT License - see LICENSE for details.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ghoststream-1.2.3.tar.gz.
File metadata
- Download URL: ghoststream-1.2.3.tar.gz
- Upload date:
- Size: 152.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d0d4c2b78b9af3b2769147fcfb55c5f7d11aa4dce1d4fabff830e390646f8ef9
|
|
| MD5 |
3768aad0ec5c4014a883142af0655155
|
|
| BLAKE2b-256 |
274de49a31e0cbf0f04a1410f3ac8026d60a339337cf3e8f8bb6b43514a21133
|
File details
Details for the file ghoststream-1.2.3-py3-none-any.whl.
File metadata
- Download URL: ghoststream-1.2.3-py3-none-any.whl
- Upload date:
- Size: 176.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c64e9232c9b6f364ebbde55bb69ea1c59a33a7c2920831334dc381b51b62204d
|
|
| MD5 |
b01f04a5bad120bc702bae669097d5f0
|
|
| BLAKE2b-256 |
897696e2dbcc1a2b9e296eda847069f17f6d7275c84ca1a733eb2f4babe0a51a
|