A lightweight reverse proxy designed for Emby private servers provide transcode via local NAS
4.1K
https://github.com/tces1/emby_transcoder
Emby-Transcoder is a lightweight Go reverse proxy that adds local FFmpeg HLS transcoding fallback for Emby and Jellyfin clients.
It is intentionally narrow: normal API traffic is forwarded to the upstream server, while selected clients can receive a proxy-provided HLS TranscodingUrl when they request PlaybackInfo.
Emby / Jellyfin client
|
v
Emby-Transcoder
- transparent proxy for ordinary requests
- PlaybackInfo matching by client profile
- PlaybackInfo rewrite to local HLS
- local FFmpeg transcode sessions
|
v
upstream Emby / Jellyfin
User-Agent, X-Emby-Authorization, and X-MediaBrowser-Token./streambridge/transcode/.AudioStreamIndex, with local transcode restart on audio changes./Sessions/Playing* check-ins plus HLS access.Not included: virtual libraries, RSS, cover generation, scraping, database storage, or a management UI.
go run ./cmd/emby-transcoder -config config.example.json
Point a client at the proxy listen address, for example http://linux-host:8097.
Use the public Docker Hub image tces1/emby_transcoder:latest. The compose file in docker/ already points at this published linux/amd64 image and does not build locally.
Minimal compose service:
services:
emby-transcoder:
image: tces1/emby_transcoder:latest
container_name: emby-transcoder
restart: unless-stopped
privileged: true
ports:
- "8097:8097"
devices:
- /dev/dri:/dev/dri
volumes:
- ./config/config.json:/app/config/config.json:ro
- ./data/transcode:/var/lib/emby-transcoder/transcode
cd docker
mkdir -p data/transcode
cp config/config.json config/config.local.json
Edit docker/config/config.local.json before starting:
upstream.url to your Emby or Jellyfin serverserver.public_url if clients reach the proxy through another reverse proxyserver.debug as false for concise logs, or set it to true for detailed diagnosticstranscode.hardware_decode as "" to disable hardware acceleration and use CPU transcodingtranscode.hardware_decode to vaapi on Linux hosts with Intel or AMD /dev/dri VAAPI supporth264_vaapi hardware encoding and does not add a scale filterh264_vaapi, and fail startup if the device, driver, or ffmpeg support is missingUpdate docker/docker-compose.yml to mount the local config file if you use config.local.json:
volumes:
- ./config/config.local.json:/app/config/config.json:ro
- ./data/transcode:/var/lib/emby-transcoder/transcode
Start or update the service:
docker compose pull
docker compose up -d
docker compose logs -f
Stop the service:
docker compose down
go build ./cmd/emby-transcoder
Copy config.example.json and change the upstream URL:
{
"server": {
"listen": ":8097",
"public_url": "",
"debug": false
},
"upstream": {
"url": "http://127.0.0.1:8096"
},
"transcode": {
"enabled": true,
"ffmpeg_path": "/usr/bin/ffmpeg",
"temp_dir": "/var/lib/emby-transcoder/transcode",
"hardware_decode": "",
"hardware_device": "/dev/dri/renderD128",
"max_sessions": 2,
"buffer_pause_seconds": 300,
"buffer_resume_seconds": 120,
"segment_seconds": 2,
"segment_retention_seconds": 300,
"idle_timeout_seconds": 60
}
}
Leave public_url empty when clients connect directly to Emby-Transcoder. Set it when Emby-Transcoder sits behind another reverse proxy.
Leave debug as false for concise action-level logs. Set it to true when you want detailed TRACE_SWITCH and request-level diagnostics.
Leave hardware_decode as "" to disable hardware acceleration and use CPU transcoding. Set it to vaapi to enable VAAPI hardware transcoding. The default hardware_device is /dev/dri/renderD128.
The current VAAPI path uses hardware decode plus h264_vaapi hardware encoding and does not add a scale filter. If the device, driver, or h264_vaapi probe fails, startup stops with an error.
Emby-Transcoder keeps local FFmpeg sessions tied to Emby playback check-ins:
POST /Sessions/Playing and /Sessions/Playing/Progress update local playback state.POST /Sessions/Playing/Stopped immediately stops the matching local FFmpeg session.segment_seconds controls HLS segment duration; default 2 balances startup latency with segment count, while 1 is fastest and higher values reduce disk churn.buffer_pause_seconds ahead of playback, FFmpeg is paused.buffer_resume_seconds, FFmpeg resumes.segment_retention_seconds behind the current playback position are deleted from the local cache.idle_timeout_seconds, the idle reaper stops the session.master.m3u8 request with a different upstream stream URL, such as a seek with a different StartTimeTicks, restarts the local session.This repository's code is licensed under the Apache License 2.0, Copyright 2026 tces1.
Debian, FFmpeg, and VAAPI userspace components included in the Docker image remain under their respective upstream licenses.
Content type
Image
Digest
sha256:ffcb8ab74…
Size
209 MB
Last updated
about 2 months ago
docker pull tces1/emby_transcoder