Simple WS/HTTP server for re-streaming video (RTSP) to client in MSE/HLS format.
It is highly inspired by https://github.com/deepch and his projects. So why am I trying to reinvent the wheel? Well, I'm just trying to fit my needs.
Linux - link
go get github.com/LdDl/video-server
# or just clone it
# git clone https://github.com/LdDl/video-server.gitGo to root folder of downloaded repository, move to cmd/video_server folder:
cd $CLONED_PATH/cmd/video_server
go build -o video_server main.govideo_server -h-conf string
Path to configuration either TOML-file or JSON-file (default "conf.toml")
-cpuprofile file
write cpu profile to file
-memprofile file
write memory profile to filePrepare configuration file (example here). Then run binary:
video_server --conf=conf.tomlFor HLS-based player go to hls-subdirectory.
For MSE-based (websockets) player go to mse-subdirectory.
Then follow this set of commands:
npm install
export NODE_OPTIONS=--openssl-legacy-provider
npm run devYou will se something like this after succesfull fron-end start:
DONE Compiled successfully in 1783ms 12:09:30 PM
App running at:
- Local: http://localhost:8080/ Paste link to the browser and check if video loaded successfully.
You can configure application to write MP4 chunks of custom duration (but not less than first keyframe duration) to the filesystem or S3 MinIO. The archive system supports two independent modes: recording (writing new segments) and serving (playback of existing archive files).
The archive configuration has two separate flags:
recording- Enables writing new archive segments. Whentrue, the server will continuously record video stream chunks to storage.serving- Enables playback of existing archive files via WebSocket. Whentrue, clients can request archive playback even if recording is disabled.
This allows flexible deployment scenarios:
recording = true, serving = true- Full archive functionality (record and playback)recording = true, serving = false- Record only, no playback APIrecording = false, serving = true- Playback only from existing archive files (useful for read-only archive access)recording = false, serving = false- Archive completely disabled
Global archive settings provide defaults for all streams:
[archive]
recording = true
serving = true
directory = "./mp4"
ms_per_file = 30000Each stream can override global settings. Per-stream settings take precedence over global configuration.
For filesystem storage:
[[rtsp_streams]]
# ...
# Some other single stream props
# ...
archive = { recording = true, ms_per_file = 20000, type = "filesystem", directory = "custom_folder" }For S3 MinIO storage:
[[rtsp_streams]]
# ...
# Some other single stream props
# ...
archive = { recording = true, ms_per_file = 20000, type = "minio", directory = "custom_folder", minio_bucket = "vod-bucket", minio_path = "/var/archive_data_custom" }When a client requests archive playback:
- The server first checks per-stream archive configuration
- If per-stream storage is not available, it falls back to global archive settings
- Global
servingflag must betruefor fallback to work
This allows you to:
- Record with per-stream settings but serve using global directory
- Serve pre-existing archive files without per-stream configuration
For storing archive to S3 MinIO, configure both filesystem (for temporary files) and MinIO settings:
[archive]
recording = true
serving = true
directory = "./mp4"
ms_per_file = 30000
minio_settings = { host = "localhost", port = 29199, user = "minio_secret_login", password = "minio_secret_password", default_bucket = "archive-bucket", default_path = "/var/archive_data" }To install MinIO you can use ./docker-compose.yaml or [./scripts/minio-ansible.yml](Ansible script) for example of deployment workflows.
The server provides a REST API endpoint to query available archive time ranges:
GET /archive/:stream_id/ranges
Response example:
{
"stream_id": "0742091c-19cd-4658-9b4f-5320da160f45",
"ranges": [
{
"start": "2025-12-20T10:00:00Z",
"end": "2025-12-20T12:30:00Z"
},
{
"start": "2025-12-20T14:00:00Z",
"end": "2025-12-20T18:00:00Z"
}
]
}GIN web-framework - https://github.com/gin-gonic/gin. License is MIT
Media library - http://github.com/deepch/vdk. License is MIT.
UUID generation and parsing - https://github.com/google/uuid. License is BSD 3-Clause
Websockets - https://github.com/gorilla/websocket. License is BSD 2-Clause
m3u8 library - https://github.com/grafov/m3u8. License is BSD 3-Clause
Working with mp4 files - https://github.com/Eyevinn/mp4ff. License is MIT
errors wrapping - https://github.com/pkg/errors . License is BSD 2-Clause
You can check it here
Roman - https://github.com/webver
Pavel - https://github.com/Pavel7824
Dimitrii Lopanov - https://github.com/LdDl
Morozka - https://github.com/morozka