videoserver

package module
v0.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 25, 2026 License: MIT Imports: 42 Imported by: 0

README

GoDoc Sourcegraph Go Report Card GitHub tag

Golang-based video-server for re-streaming RTSP to HLS/MSE

Table of Contents

About

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.

Installation

Pre-built binaries

Download the archive for your platform from the latest release:

Platform Archive
Linux amd64 linux-amd64-video_server.tar.gz
Linux arm64 linux-arm64-video_server.tar.gz
macOS amd64 darwin-amd64-video_server.tar.gz
macOS arm64 darwin-arm64-video_server.tar.gz
Windows amd64 windows-amd64-video_server.zip

Each archive contains video_server or video_server.exe. Extract it into a directory in your PATH.

Quick installation on Linux amd64:

curl -fsSL https://github.com/LdDl/video-server/releases/latest/download/linux-amd64-video_server.tar.gz \
  | sudo tar -xz -C /usr/local/bin video_server

For Linux arm64, replace linux-amd64 with linux-arm64. On macOS, use darwin-amd64 for Intel or darwin-arm64 for Apple Silicon.

If you would rather run the server in a container, see Docker.

From source
go install github.com/LdDl/video-server/cmd/video_server@latest

Or clone the repository and build it yourself:

git clone https://github.com/LdDl/video-server.git
cd video-server
go build -o video_server ./cmd/video_server

Usage

video_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 file
Start server

Prepare configuration file (example here). Then run binary:

video_server --conf=conf.toml
Test Client-Server

For 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 dev

You 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.

Docker

The image is published as dimahkiin/video_server.

The server does not read environment variables, so a configuration file has to be mounted into the container. Both servers must listen on 0.0.0.0 inside it, otherwise the published ports stay unreachable:

[api]
host = "0.0.0.0"
port = 8091

[video]
host = "0.0.0.0"
port = 8090

Start the latest image with that configuration file:

docker run --rm --name video_server --stop-timeout 30 \
  -p 127.0.0.1:8090:8090 -p 127.0.0.1:8091:8091 \
  --mount type=bind,source="$(pwd)/conf.toml",target=/app/conf.toml,readonly \
  -v video_server_hls:/app/hls \
  -v video_server_mp4:/app/mp4 \
  docker.io/dimahkiin/video_server:latest -conf /app/conf.toml

Port 8090 serves MSE websockets and HLS files, port 8091 serves the REST API. To accept connections from other hosts, drop the 127.0.0.1: prefix from the port mappings. The named volumes keep HLS segments and archive files across container recreation; skip them when neither HLS nor recording is enabled.

Docker Compose

From the repository root, use the published image with the example configuration:

VIDEO_SERVER_IMAGE=dimahkiin/video_server:latest \
docker compose up --no-build --pull always

VIDEO_SERVER_CONFIG selects the configuration file (use an absolute path for a file outside the repository), VIDEO_SERVER_PORT and VIDEO_SERVER_API_PORT change the published host ports, and VIDEO_SERVER_BIND_HOST changes the host bind address. The video_server_hls and video_server_mp4 volumes survive container recreation.

VIDEO_SERVER_IMAGE=dimahkiin/video_server:latest \
VIDEO_SERVER_CONFIG=/etc/video_server/conf.toml \
VIDEO_SERVER_BIND_HOST=0.0.0.0 \
docker compose up -d --no-build --pull always

The docker-compose.yaml file is unrelated to the server itself: it starts a MinIO instance for the archive, see MinIO Configuration.

On-demand streams

By default every configured stream is pulled from its source all the time, whether anybody is watching or not. For a fleet of cameras that nobody looks at most of the day this is a lot of wasted inbound traffic and CPU.

With on-demand mode the upstream connection (RTSP session or local file reader) is opened when the first viewer arrives and closed a little while after the last one leaves:

  • MSE: the WebSocket connection is the viewer. The first connection dials the source, the last disconnect arms the idle timer.
  • HLS: there is no persistent connection, so every playlist/segment request counts as activity. The stream stays alive while requests keep coming. The very first playlist request of a cold stream waits for the first segment to be cut (about two ms_per_segment), so expect a start-up delay of that order.
  • Recording: a stream with archive.recording = true is never released, otherwise the archive would have gaps.
[on_demand]
# default for all streams
enabled = true
# keep the upstream alive this long after the last viewer
idle_ms = 30000
# probe idle sources so that `online` stays meaningful
health_check = true
health_interval_ms = 30000
health_timeout_ms = 3000

[[rtsp_streams]]
guid = "..."
url = "rtsp://..."
output_types = ["mse"]
# per-stream override: this one is always on
on_demand = false

The first viewer of a cold stream waits for the source to answer (typically 1-3 seconds for an RTSP camera) before video starts.

Health check

An idle on-demand stream is not connected, so the server would not know whether the camera is alive. When health_check is enabled, idle sources are probed every health_interval_ms with a plain RTSP DESCRIBE (Basic and Digest auth are supported). No PLAY is sent, so the camera never starts streaming media for a probe. Local-file sources are checked for file existence. Streams with a live upstream are not probed: the session itself is the health signal.

Status fields

GET /status reports, per stream:

  • on_demand - whether the stream is lazily connected
  • online - last known reachability of the source (from the live session or from the health probe)
  • streaming - whether the upstream loop is running right now
  • viewers - number of attached MSE clients
  • last_health_check, last_health_error - when the source was last checked and what went wrong, if anything
  • status - kept for compatibility: true while a live session has codec data

online = true, streaming = false, viewers = 0 is the normal state of a healthy camera that nobody is watching.

Archive

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).

Recording vs Serving

The archive configuration has two separate flags:

  • recording - Enables writing new archive segments. When true, the server will continuously record video stream chunks to storage.
  • serving - Enables playback of existing archive files via WebSocket. When true, 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 API
  • recording = false, serving = true - Playback only from existing archive files (useful for read-only archive access)
  • recording = false, serving = false - Archive completely disabled
Global Configuration

Global archive settings provide defaults for all streams:

[archive]
recording = true
serving = true
directory = "./mp4"
ms_per_file = 30000
Per-Stream Configuration

Each 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" }
Configuration Fallback

When a client requests archive playback:

  1. The server first checks per-stream archive configuration
  2. If per-stream storage is not available, it falls back to global archive settings
  3. Global serving flag must be true for 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
MinIO 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.

Archive REST API

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"
    }
  ]
}

Dependencies

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

License

You can check it here

Developers

Roman - https://github.com/webver

Pavel - https://github.com/Pavel7824

Dimitrii Lopanov - https://github.com/LdDl

Morozka - https://github.com/morozka

Documentation

Index

Constants

View Source
const (
	SCOPE_APP           = "app"
	SCOPE_CONFIGURATION = "configuration"
	SCOPE_STREAM        = "stream"
	SCOPE_STREAMING     = "streaming"
	SCOPE_WS_HANDLER    = "ws_handler"
	SCOPE_API_SERVER    = "api_server"
	SCOPE_WS_SERVER     = "ws_server"
	SCOPE_ARCHIVE       = "archive"
	SCOPE_MP4           = "mp4"
	SCOPE_HLS           = "hls"
	SCOPE_HEALTH        = "health"

	EVENT_APP_CORS_CONFIG = "app_cors_config"

	EVENT_STREAM_CODEC_ADD     = "stream_codec_add"
	EVENT_STREAM_STATUS_UPDATE = "stream_status_update"
	EVENT_STREAM_CLIENT_ADD    = "stream_client_add"
	EVENT_STREAM_CLIENT_DELETE = "stream_client_delete"
	EVENT_STREAM_CAST_PACKET   = "stream_cast"

	EVENT_STREAMING_RUN                 = "streaming_run"
	EVENT_STREAMING_START               = "streaming_start"
	EVENT_STREAMING_DONE                = "streaming_done"
	EVENT_STREAMING_RESTART             = "streaming_restart"
	EVENT_STREAMING_DIAL                = "streaming_dial"
	EVENT_STREAMING_STATUS_UPDATE       = "streaming_status_update"
	EVENT_STREAMING_PACKET_SIGNAL       = "streaming_packet_signal"
	EVENT_STREAMING_STOP_SIGNAL         = "streaming_stop_signal"
	EVENT_STREAMING_UNKNOWN_SIGNAL      = "streaming_unknown_signal"
	EVENT_STREAMING_CODEC_UPDATE_SIGNAL = "streaming_codec_update_signal"
	EVENT_STREAMING_EXIT_SIGNAL         = "streaming_codec_exit_signal"
	EVENT_STREAMING_CODEC_MET           = "streaming_codec_met"
	EVENT_STREAMING_AUDIO_MET           = "streaming_audio_met"
	EVENT_STREAMING_HLS_CAST            = "streaming_hls_cast"
	EVENT_STREAMING_MP4_CAST            = "streaming_mp4_cast"
	EVENT_STREAMING_EOF                 = "streaming_eof"
	EVENT_STREAMING_LOOP                = "streaming_loop"
	EVENT_STREAMING_COMPLETE            = "streaming_complete"
	EVENT_STREAMING_ACQUIRE             = "streaming_acquire"
	EVENT_STREAMING_RELEASE             = "streaming_release"
	EVENT_STREAMING_IDLE_STOP           = "streaming_idle_stop"
	EVENT_STREAMING_CANCEL              = "streaming_cancel"

	EVENT_HEALTH_START = "health_start"
	EVENT_HEALTH_PROBE = "health_probe"

	EVENT_API_PREPARE     = "api_server_prepare"
	EVENT_API_START       = "api_server_start"
	EVENT_API_CORS_ENABLE = "api_server_cors_enable"
	EVENT_API_REQUEST     = "api_request"

	EVENT_WS_PREPARE     = "ws_server_prepare"
	EVENT_WS_START       = "ws_server_start"
	EVENT_WS_CORS_ENABLE = "ws_server_cors_enable"
	EVENT_WS_REQUEST     = "ws_request"
	EVENT_WS_UPGRADER    = "ws_upgrader"
	EVENT_WS_PING        = "ws_ping"

	EVENT_HLS_START_CAST              = "hls_start_cast"
	EVENT_HLS_PLAYLIST_PREPARE        = "hls_playlist_prepare"
	EVENT_HLS_PLAYLIST_CREATE         = "hls_playlist_create"
	EVENT_HLS_PLAYLIST_RESTART        = "hls_playlist_restart"
	EVENT_HLS_CLOSE_FILE              = "hls_close_file"
	EVENT_HLS_WRITE_TRAIL             = "hls_write_trail"
	EVENT_HLS_REMOVE_OUTDATED         = "hls_remove_outdated"
	EVENT_HLS_REMOVE_OUTDATED_SEGMENT = "hls_remove_outdated_segment"
	EVENT_HLS_REMOVE_CHUNK            = "hls_remove_chunk"

	EVENT_ARCHIVE_START_CAST  = "archive_start_cast"
	EVENT_ARCHIVE_CREATE_FILE = "archive_create_file"
	EVENT_ARCHIVE_CLOSE_FILE  = "archive_close_file"
	EVENT_CHAN_PACKET         = "mp4_chan_pck"
	EVENT_CHAN_STOP           = "mp4_chan_stop"
	EVENT_CHAN_KEYFRAME       = "mp4_chan_keyframe"
	EVENT_SEGMENT_CUT         = "mp4_segment_cut"
	EVENT_NO_START            = "mp4_no_start"
	EVENT_MP4_WRITE           = "mp4_write"
	EVENT_MP4_WRITE_TRAIL     = "mp4_write_trail"
	EVENT_MP4_SAVE_MINIO      = "mp4_save_minio"
	EVENT_MP4_CLOSE           = "mp4_close"
)
View Source
const (
	STOP_SIGNAL_ERR = StopSignal(iota)
	STOP_SIGNAL_NO_VIDEO
	STOP_SIGNAL_DISCONNECT
	STOP_SIGNAL_STOP_DIAL
)
View Source
const (
	STREAM_TYPE_UNDEFINED = StreamType(iota)
	STREAM_TYPE_RTSP
	STREAM_TYPE_HLS
	STREAM_TYPE_MSE
	STREAM_TYPE_LOCAL_FILE
)
View Source
const (
	VERBOSE_NONE = VerboseLevel(iota)
	VERBOSE_SIMPLE
	VERBOSE_ADD
	VERBOSE_ALL
)

Variables

View Source
var (
	ErrStreamNotFound         = fmt.Errorf("stream not found for provided ID")
	ErrStreamHasNoVideo       = fmt.Errorf("stream has no video")
	ErrStreamDisconnected     = fmt.Errorf("disconnected")
	ErrStreamTypeNotExists    = fmt.Errorf("stream type does not exists")
	ErrStreamTypeNotSupported = fmt.Errorf("stream type is not supported")
	ErrNotSupportedStorage    = fmt.Errorf("not supported storage")
	ErrNullArchive            = fmt.Errorf("archive == nil")
)
View Source
var (
	ErrTimeFailure = fmt.Errorf("bad packet times")
)

Functions

func ArchiveRangesWrapper added in v0.8.0

func ArchiveRangesWrapper(app *Application, verboseLevel VerboseLevel) func(ctx *gin.Context)

ArchiveRangesWrapper returns handler for listing available archive time ranges

func ArchiveWSHandler added in v0.8.0

func ArchiveWSHandler(app *Application, wsUpgrader *websocket.Upgrader, verboseLevel VerboseLevel) func(w http.ResponseWriter, r *http.Request)

ArchiveWSHandler handles WebSocket connections for archive playback Query params: stream_id, start (unix timestamp), duration (seconds, optional) Supports chunked streaming: server sends ~5 minutes, then waits for "more" message

func DisableCamera added in v0.3.2

func DisableCamera(app *Application, verboseLevel VerboseLevel) func(ctx *gin.Context)

DisableCamera turns off stream for specific stream ID

func EnableCamera added in v0.3.2

func EnableCamera(app *Application, verboseLevel VerboseLevel) func(ctx *gin.Context)

EnableCamera adds new stream if does not exist

func HLSWrapper added in v0.2.0

func HLSWrapper(app *Application, verboseLevel VerboseLevel) func(ctx *gin.Context)

HLSWrapper returns HLS handler (static files)

func ListWrapper added in v0.2.0

func ListWrapper(app *Application, verboseLevel VerboseLevel) func(ctx *gin.Context)

ListWrapper returns list of streams

func MutexLocked added in v0.5.0

func MutexLocked(m *sync.Mutex) bool

func RWMutexLocked added in v0.5.0

func RWMutexLocked(rw *sync.RWMutex) bool

func RWMutexRLocked added in v0.5.0

func RWMutexRLocked(rw *sync.RWMutex) bool

func StatusWrapper added in v0.2.0

func StatusWrapper(app *Application, verboseLevel VerboseLevel) func(ctx *gin.Context)

StatusWrapper returns statuses for list of streams

func UploadToMinio added in v0.6.0

func UploadToMinio(minioStorage storage.ArchiveStorage, segmentName, bucket, sourceFileName string) (string, error)

func WebSocketWrapper added in v0.2.0

func WebSocketWrapper(app *Application, wsUpgrader *websocket.Upgrader, verboseLevel VerboseLevel) func(ctx *gin.Context)

WebSocketWrapper returns WS handler

Types

type APIConfiguration added in v0.4.0

type APIConfiguration struct {
	Enabled bool         `json:"-"`
	Host    string       `json:"host"`
	Port    int32        `json:"port"`
	Mode    string       `json:"-"`
	Verbose VerboseLevel `json:"-"`
}

APIConfiguration is just copy of configuration.APIConfiguration but with some not exported fields

type Application added in v0.2.0

type Application struct {
	APICfg         APIConfiguration   `json:"api"`
	VideoServerCfg VideoConfiguration `json:"video"`
	Streams        StreamsStorage     `json:"streams"`
	HLS            HLSInfo            `json:"hls"`
	OnDemand       OnDemandInfo       `json:"on_demand"`
	// Global archive config for playback fallback
	ArchiveConfig ArchiveInfo  `json:"-"`
	CorsConfig    *cors.Config `json:"-"`
	// contains filtered or unexported fields
}

Application is a configuration parameters for application

func NewApplication added in v0.2.0

func NewApplication(cfg *configuration.Configuration) (*Application, error)

NewApplication Prepare configuration for application

func (*Application) GetArchiveStorageForPlayback added in v0.8.0

func (app *Application) GetArchiveStorageForPlayback(streamID uuid.UUID) (*StreamArchiveWrapper, error)

GetArchiveStorageForPlayback returns archive storage for playback. First tries stream-specific archive, then falls back to global archive config.

func (*Application) RunStream added in v0.5.0

func (app *Application) RunStream(ctx context.Context, streamID uuid.UUID) error

RunStream runs single video stream loop until the context is cancelled

func (*Application) StartAPIServer added in v0.3.2

func (app *Application) StartAPIServer() error

StartAPIServer starts server with API functionality

func (*Application) StartHealthMonitor added in v0.9.0

func (app *Application) StartHealthMonitor(ctx context.Context)

StartHealthMonitor periodically probes idle on-demand streams so that their `online` flag stays meaningful while no video is being pulled. Streams with a live upstream are skipped: the session itself is the health signal for them. Returns immediately when health checks are disabled

func (*Application) StartStream added in v0.3.2

func (app *Application) StartStream(streamID uuid.UUID)

StartStream starts the upstream loop for the given stream unless it is on-demand and nobody needs it yet

func (*Application) StartStreams added in v0.2.0

func (app *Application) StartStreams()

StartStreams starts upstream loops for permanent streams. On-demand streams wait for their first viewer

func (*Application) StartVideoServer added in v0.3.2

func (app *Application) StartVideoServer()

StartVideoServer initializes "video" server and run it (MSE-websockets and HLS-static files)

type ArchiveInfo added in v0.8.0

type ArchiveInfo struct {
	Recording    bool
	Serving      bool
	Directory    string
	MsPerSegment int64
}

ArchiveInfo stores global archive configuration

type ArchiveRangesResponse added in v0.8.0

type ArchiveRangesResponse struct {
	StreamID string      `json:"stream_id"`
	Ranges   []TimeRange `json:"ranges"`
}

ArchiveRangesResponse is the API response for available time ranges

type EnablePostData added in v0.3.2

type EnablePostData struct {
	GUID        uuid.UUID `json:"guid"`
	URL         string    `json:"url"`
	OutputTypes []string  `json:"output_types"`
	// OnDemand overrides the global on-demand default for the new stream. Omit to inherit
	OnDemand *bool `json:"on_demand"`
}

EnablePostData is a POST-body for API which enables to turn on/off specific streams

type HLSInfo added in v0.4.0

type HLSInfo struct {
	MsPerSegment int64  `json:"hls_ms_per_segment"`
	Directory    string `json:"-"`
	WindowSize   uint   `json:"hls_window_size"`
	Capacity     uint   `json:"hls_window_capacity"`
}

HLSInfo is an information about HLS parameters for server

type MP4Demuxer added in v0.7.0

type MP4Demuxer struct {
	// contains filtered or unexported fields
}

MP4Demuxer wraps mp4ff to provide av.Packet compatible output

func NewMP4Demuxer added in v0.7.0

func NewMP4Demuxer(filePath string) (*MP4Demuxer, error)

NewMP4Demuxer creates a new demuxer for the given file

func (*MP4Demuxer) Close added in v0.7.0

func (d *MP4Demuxer) Close() error

Close closes the demuxer and underlying file

func (*MP4Demuxer) ReadPacket added in v0.7.0

func (d *MP4Demuxer) ReadPacket() (av.Packet, error)

ReadPacket reads the next packet from the demuxer

func (*MP4Demuxer) SeekToStart added in v0.7.0

func (d *MP4Demuxer) SeekToStart()

SeekToStart resets all tracks to the beginning

func (*MP4Demuxer) Streams added in v0.7.0

func (d *MP4Demuxer) Streams() []av.CodecData

Streams returns the codec data for all tracks

type MP4ffMuxer added in v0.7.0

type MP4ffMuxer struct {
	// contains filtered or unexported fields
}

MP4ffMuxer wraps mp4ff library to create MP4 files

func NewMP4ffMuxer added in v0.7.0

func NewMP4ffMuxer(w io.WriteSeeker) *MP4ffMuxer

NewMP4ffMuxer creates a new muxer using mp4ff library

func (*MP4ffMuxer) Duration added in v0.7.0

func (m *MP4ffMuxer) Duration() time.Duration

Duration returns the duration of the longest track (relative, not absolute)

func (*MP4ffMuxer) WriteHeader added in v0.7.0

func (m *MP4ffMuxer) WriteHeader(codecs []av.CodecData) error

WriteHeader initializes the muxer with codec information

func (*MP4ffMuxer) WritePacket added in v0.7.0

func (m *MP4ffMuxer) WritePacket(pkt av.Packet) error

WritePacket writes a packet to the muxer

func (*MP4ffMuxer) WriteTrailer added in v0.7.0

func (m *MP4ffMuxer) WriteTrailer() error

WriteTrailer finalizes the file with moov box

type OnDemandInfo added in v0.9.0

type OnDemandInfo struct {
	Enabled        bool
	IdleTimeout    time.Duration
	HealthCheck    bool
	HealthInterval time.Duration
	HealthTimeout  time.Duration
}

OnDemandInfo is the resolved on-demand / health-check configuration

func (OnDemandInfo) MarshalJSON added in v0.9.0

func (info OnDemandInfo) MarshalJSON() ([]byte, error)

MarshalJSON reports durations in milliseconds, matching the configuration file

type ServerInfo added in v0.2.0

type ServerInfo struct {
	HTTPAddr      string `json:"http_addr"`
	VideoHTTPPort int32  `json:"http_port"`
	APIHTTPPort   int32  `json:"-"`
}

ServerInfo is an information about server

type StopSignal added in v0.6.0

type StopSignal uint8

type StreamArchiveWrapper added in v0.6.0

type StreamArchiveWrapper struct {
	// contains filtered or unexported fields
}

type StreamConfiguration

type StreamConfiguration struct {
	URL    string `json:"url"`
	Status bool   `json:"status"`
	// OnDemand is true when the upstream is pulled only while there is demand (viewers or recording)
	OnDemand bool `json:"on_demand"`
	// Online is the last known health of the source. For a streaming source it follows the live session,
	// for an idle on-demand source it is set by the health monitor
	Online bool `json:"online"`
	// Streaming is true while the upstream loop is running (dialing or receiving)
	Streaming bool `json:"streaming"`
	// Viewers is the number of MSE clients currently attached
	Viewers              int                  `json:"viewers"`
	LastHealthCheck      time.Time            `json:"last_health_check"`
	LastHealthError      string               `json:"last_health_error"`
	SupportedOutputTypes []StreamType         `json:"supported_output_types"`
	Codecs               []av.CodecData       `json:"codecs"`
	Clients              map[uuid.UUID]viewer `json:"-"`
	// contains filtered or unexported fields
}

StreamConfiguration is a configuration parameters for specific stream

func NewStreamConfiguration added in v0.4.0

func NewStreamConfiguration(streamURL string, supportedTypes []StreamType) *StreamConfiguration

NewStreamConfiguration returns default configuration

type StreamInfoShorten added in v0.4.0

type StreamInfoShorten struct {
	StreamID string `json:"stream_id"`
}

type StreamType added in v0.5.0

type StreamType uint16

func (StreamType) String added in v0.5.0

func (iotaIdx StreamType) String() string

type StreamsInfoShortenList added in v0.4.0

type StreamsInfoShortenList struct {
	Data []StreamInfoShorten `json:"data"`
}

type StreamsStorage added in v0.4.0

type StreamsStorage struct {
	sync.RWMutex
	// contains filtered or unexported fields
}

StreamsStorage Map wrapper for map[uuid.UUID]*StreamConfiguration with mutex for concurrent usage

func NewStreamsStorageDefault added in v0.4.0

func NewStreamsStorageDefault() StreamsStorage

NewStreamsStorageDefault prepares new allocated storage

func (*StreamsStorage) AddCodecForStream added in v0.5.2

func (streams *StreamsStorage) AddCodecForStream(streamID uuid.UUID, codecs []av.CodecData) error

AddCodecForStream appends new codecs data for the given stream

func (*StreamsStorage) AddViewer added in v0.5.2

func (streams *StreamsStorage) AddViewer(streamID uuid.UUID) (uuid.UUID, chan av.Packet, error)

AddViewer adds client to the given stream. Return newly client ID, buffered channel for stream on success

func (*StreamsStorage) CastPacket added in v0.5.2

func (streams *StreamsStorage) CastPacket(streamID uuid.UUID, pck av.Packet, hlsEnabled, archiveEnabled bool) error

CastPacket cast AV Packet to viewers and possible to HLS/MP4 channels. The read lock is held for the whole cast: every send below is non-blocking, and the viewers map is mutated by AddViewer/DeleteViewer under the write lock, so iterating it unlocked would race

func (*StreamsStorage) DeleteViewer added in v0.5.2

func (streams *StreamsStorage) DeleteViewer(streamID, clientID uuid.UUID)

DeleteViewer removes given client from the stream

func (*StreamsStorage) GetAllStreamsIDS added in v0.5.2

func (streams *StreamsStorage) GetAllStreamsIDS() []uuid.UUID

GetAllStreamsIDS returns all storage streams' keys as slice

func (*StreamsStorage) GetCodecsDataForStream added in v0.5.2

func (streams *StreamsStorage) GetCodecsDataForStream(streamID uuid.UUID) ([]av.CodecData, error)

GetCodecsDataForStream returns COPY of codecs data for the given stream

func (*StreamsStorage) GetStreamArchiveStorage added in v0.5.2

func (streams *StreamsStorage) GetStreamArchiveStorage(streamID uuid.UUID) *StreamArchiveWrapper

GetStreamArchiveStorage returns pointer to the archive storage for the given stream

func (*StreamsStorage) GetStreamInfo added in v0.5.2

func (streams *StreamsStorage) GetStreamInfo(streamID uuid.UUID) (string, []StreamType)

GetStreamInfo returns stream URL and its supported output types

func (*StreamsStorage) GetVerboseLevelForStream added in v0.5.2

func (streams *StreamsStorage) GetVerboseLevelForStream(streamID uuid.UUID) VerboseLevel

GetVerboseLevelForStream returst verbose level for the given stream

func (*StreamsStorage) IsArchiveEnabledForStream added in v0.5.2

func (streams *StreamsStorage) IsArchiveEnabledForStream(streamID uuid.UUID) (bool, error)

IsArchiveEnabledForStream returns whenever archive has been enabled for stream

func (*StreamsStorage) MarshalJSON added in v0.9.0

func (streams *StreamsStorage) MarshalJSON() ([]byte, error)

MarshalJSON renders the storage as a map keyed by stream ID, so that /status actually lists the streams

func (*StreamsStorage) StreamExists added in v0.5.2

func (streams *StreamsStorage) StreamExists(streamID uuid.UUID) bool

StreamExists checks whenever given stream ID exists in storage

func (*StreamsStorage) TypeExistsForStream added in v0.5.2

func (streams *StreamsStorage) TypeExistsForStream(streamID uuid.UUID, streamType StreamType) bool

TypeExistsForStream checks whenever specific stream ID supports then given output stream type

func (*StreamsStorage) UpdateArchiveStorageForStream added in v0.5.2

func (streams *StreamsStorage) UpdateArchiveStorageForStream(streamID uuid.UUID, archiveStorage *StreamArchiveWrapper) error

UpdateArchiveStorageForStream updates archive storage configuration (it override existing one!)

func (*StreamsStorage) UpdateStreamHealth added in v0.9.0

func (streams *StreamsStorage) UpdateStreamHealth(streamID uuid.UUID, online bool, lastError string) error

UpdateStreamHealth records the latest known reachability of the stream source

func (*StreamsStorage) UpdateStreamStatus added in v0.5.2

func (streams *StreamsStorage) UpdateStreamStatus(streamID uuid.UUID, status bool) error

UpdateStreamStatus sets new status value for the given stream

type TimeRange added in v0.8.0

type TimeRange struct {
	Start time.Time `json:"start"`
	End   time.Time `json:"end"`
}

TimeRange represents a continuous time range of available archive

type VerboseLevel added in v0.5.0

type VerboseLevel uint16

func NewVerboseLevelFrom added in v0.5.0

func NewVerboseLevelFrom(str string) VerboseLevel

type VideoConfiguration added in v0.4.0

type VideoConfiguration struct {
	Host    string       `json:"host"`
	Port    int32        `json:"port"`
	Mode    string       `json:"-"`
	Verbose VerboseLevel `json:"-"`
}

VideoConfiguration is just copy of configuration.VideoConfiguration but with some not exported fields

Directories

Path Synopsis
cmd
video_server command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL