videoserver

package module
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Oct 25, 2024 License: MIT Imports: 30 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.

Instalation

Binaries

Linux - link

From source
go get github.com/LdDl/video-server
# or just clone it
# git clone https://github.com/LdDl/video-server.git

Go to root folder of downloaded repository, move to cmd/video_server folder:

cd $CLONED_PATH/cmd/video_server
go build -o video_server main.go

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.

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

  • For storing archive to the filesystem. Point default directory for storing MP4 files and duration:

    [archive]
    enabled = true
    directory = "./mp4"
    ms_per_file = 30000
    

    For each stream configuration you can override default directory and duration. Field "type" should have value "filesystem":

    [[rtsp_streams]]
    # ...
    # Some other single stream props
    # ...
    archive = { enabled = true, ms_per_file = 20000, type = "filesystem", directory = "custom_folder" } 
    
  • For storing archive to the S3 MinIO: Modify configuration file to have both filesystem and minio configuration (filesystem will be picked for storing temporary files before moving it to the MinIO), e.g.:

    [archive]
    enabled = 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" }
    

    For each stream configuration you can override default directory for temporary files, MinIO bucket and path in it and chunk duration. Field "type" should have value "minio":

    [[rtsp_streams]]
    # ...
    # Some other single stream props
    # ...
    archive = { enabled = true, ms_per_file = 20000, type = "minio", "directory": "custom_folder", minio_bucket = "vod-bucket", minio_path = "/var/archive_data_custom" }
    
  • If you want disable archive for specified stream, just set value of the field enabled to false in streams array. For disabling archive at all you can do the same but in the main configuration (where default values are set)

  • To install MinIO (in case if you want to store archive in S3) you can use [./docker-compose.yaml](docker-compose file) or [./scripts/minio-ansible.yml](Ansible script) for example of deployment workflows

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

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"

	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_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
)
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 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(hlsConf *HLSInfo, 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(streamsStorage *StreamsStorage, 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"`
	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) RunStream added in v0.5.0

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

func (*Application) StartAPIServer added in v0.3.2

func (app *Application) StartAPIServer() error

StartAPIServer starts server with API functionality

func (*Application) StartStream added in v0.3.2

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

StartStream starts single video stream

func (*Application) StartStreams added in v0.2.0

func (app *Application) StartStreams()

StartStreams starts all video streams

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 EnablePostData added in v0.3.2

type EnablePostData struct {
	GUID        uuid.UUID `json:"guid"`
	URL         string    `json:"url"`
	OutputTypes []string  `json:"output_types"`
}

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 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"`
	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

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

Jump to

Keyboard shortcuts

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