Skip to content

BleedingXiko/GhostStream

Repository files navigation

GhostStream

Open-Source Video Transcoding Server

License: MIT Python 3.9+ Platform

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 + public models/contracts
pip install "ghoststream[server]"      # Add the server runtime dependencies

python -m ghoststream requires the server extra (or the full repo requirements.txt). pip install ghoststream by itself is the GhostHub/client SDK install.

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:

  1. Commit and push from within ghoststream/specter.
  2. 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.

About

GhostStream turns any PC into a GPU–powered transcoding backend for your self-hosted apps. HLS, ABR, HDR→SDR, hardware detection, zero config.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors