diff --git a/api/.redocly.yaml b/api/.redocly.yaml new file mode 100644 index 00000000..030d1424 --- /dev/null +++ b/api/.redocly.yaml @@ -0,0 +1,5 @@ +extends: + - recommended + +rules: + operation-4xx-response: off diff --git a/api/openapi.yaml b/api/openapi.yaml index 63adaed6..8a8405b6 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -15,19 +15,158 @@ security: [] components: schemas: + OKStatus: + type: string + enum: [ok] + + ErrorStatus: + type: string + enum: [error] + + PathSourceType: + type: string + enum: + - hlsSource + - redirect + - rpiCameraSource + - rtmpConn + - rtmpsConn + - rtmpSource + - rtspSession + - rtspSource + - rtspsSession + - srtConn + - srtSource + - mpegtsSource + - rtpSource + - webRTCSession + - webRTCSource + + PathReaderType: + type: string + enum: + - hlsMuxer + - rpiCameraSecondary + - rtmpConn + - rtmpsConn + - rtspConn + - rtspSession + - rtspsConn + - rtspsSession + - srtConn + - webRTCSession + + PathTrackCodec: + type: string + enum: + - AV1 + - VP9 + - VP8 + - H265 + - H264 + - MPEG-4 Video + - MPEG-1 Video + - MJPEG + - Opus + - Vorbis + - MPEG-4 Audio + - MPEG-4 Audio LATM + - MPEG-1 Audio + - AC3 + - Speex + - G726 + - G722 + - G711 + - LPCM + - MPEG-TS + - KLV + - Generic + + AlwaysAvailableTrackCodec: + type: string + enum: + - AV1 + - VP9 + - H265 + - H264 + - MPEG4Audio + - Opus + - G711 + - LPCM + + AuthAction: + type: string + enum: + - publish + - read + - playback + - api + - metrics + - pprof + + AuthMethod: + type: string + enum: [internal, http, jwt] + + Encryption: + type: string + enum: [no, optional, strict] + + HLSVariant: + type: string + enum: [mpegts, fmp4, lowLatency] + + LogDestination: + type: string + enum: [stdout, file, syslog] + + LogLevel: + type: string + enum: [error, warn, info, debug] + + RecordFormat: + type: string + enum: [fmp4, mpegts] + + RTSPAuthMethod: + type: string + enum: [basic, digest] + + RTSPRangeType: + type: string + enum: ['', clock, npt, smpte] + + RTSPTransport: + type: string + enum: [udp, multicast, tcp, automatic] + + RTMPConnState: + type: string + enum: [idle, read, publish] + + RTSPSessionState: + type: string + enum: [idle, read, publish] + + SRTConnState: + type: string + enum: [idle, read, publish] + + WebRTCSessionState: + type: string + enum: [read, publish] + OK: type: object properties: status: - type: string - enum: [ok] + $ref: '#/components/schemas/OKStatus' Error: type: object properties: status: - type: string - enum: [error] + $ref: '#/components/schemas/ErrorStatus' error: type: string @@ -59,7 +198,7 @@ components: type: object properties: action: - type: string + $ref: '#/components/schemas/AuthAction' path: type: string @@ -68,11 +207,11 @@ components: properties: # General logLevel: - type: string + $ref: '#/components/schemas/LogLevel' logDestinations: type: array items: - type: string + $ref: '#/components/schemas/LogDestination' logStructured: type: boolean logFile: @@ -108,7 +247,7 @@ components: # Authentication authMethod: - type: string + $ref: '#/components/schemas/AuthMethod' authInternalUsers: type: array items: @@ -250,17 +389,21 @@ components: nullable: true items: type: string + enum: [udp, multicast, tcp] deprecated: true rtspTransports: type: array items: type: string + enum: [udp, multicast, tcp] encryption: type: string nullable: true deprecated: true + allOf: + - $ref: '#/components/schemas/Encryption' rtspEncryption: - type: string + $ref: '#/components/schemas/Encryption' rtspAddress: type: string rtspsAddress: @@ -295,12 +438,12 @@ components: type: array nullable: true items: - type: string + $ref: '#/components/schemas/RTSPAuthMethod' deprecated: true rtspAuthMethods: type: array items: - type: string + $ref: '#/components/schemas/RTSPAuthMethod' rtspUDPReadBufferSize: type: integer format: uint64 @@ -315,7 +458,7 @@ components: nullable: true deprecated: true rtmpEncryption: - type: string + $ref: '#/components/schemas/Encryption' rtmpAddress: type: string rtmpsAddress: @@ -355,7 +498,7 @@ components: hlsAlwaysRemux: type: boolean hlsVariant: - type: string + $ref: '#/components/schemas/HLSVariant' hlsSegmentCount: type: integer format: int64 @@ -461,6 +604,8 @@ components: type: string nullable: true deprecated: true + allOf: + - $ref: '#/components/schemas/RecordFormat' recordPartDuration: type: string nullable: true @@ -523,7 +668,7 @@ components: recordPath: type: string recordFormat: - type: string + $ref: '#/components/schemas/RecordFormat' recordPartDuration: type: string recordMaxPartSize: @@ -575,19 +720,21 @@ components: # RTSP source rtspTransport: - type: string + $ref: '#/components/schemas/RTSPTransport' rtspAnyPort: type: boolean sourceProtocol: type: string nullable: true deprecated: true + allOf: + - $ref: '#/components/schemas/RTSPTransport' sourceAnyPortEnable: type: boolean nullable: true deprecated: true rtspRangeType: - type: string + $ref: '#/components/schemas/RTSPRangeType' rtspRangeStart: type: string rtspUDPReadBufferSize: @@ -801,8 +948,10 @@ components: confName: type: string source: - $ref: '#/components/schemas/PathSource' + type: object nullable: true + allOf: + - $ref: '#/components/schemas/PathSource' ready: type: boolean deprecated: true @@ -823,7 +972,7 @@ components: tracks: type: array items: - type: string + $ref: '#/components/schemas/PathTrackCodec' bytesReceived: type: integer format: uint64 @@ -853,22 +1002,7 @@ components: type: object properties: type: - type: string - enum: - - hlsSource - - redirect - - rpiCameraSource - - rtmpConn - - rtmpSource - - rtspSession - - rtspSource - - rtspsSession - - srtConn - - srtSource - - mpegtsSource - - rtpSource - - webRTCSession - - webRTCSource + $ref: '#/components/schemas/PathSourceType' id: type: string @@ -876,14 +1010,7 @@ components: type: object properties: type: - type: string - enum: - - hlsMuxer - - rtmpConn - - rtspSession - - rtspsSession - - srtConn - - webRTCSession + $ref: '#/components/schemas/PathReaderType' id: type: string @@ -955,8 +1082,7 @@ components: remoteAddr: type: string state: - type: string - enum: [idle, read, publish] + $ref: '#/components/schemas/RTMPConnState' path: type: string query: @@ -1032,8 +1158,7 @@ components: remoteAddr: type: string state: - type: string - enum: [idle, read, publish] + $ref: '#/components/schemas/RTSPSessionState' path: type: string query: @@ -1107,8 +1232,7 @@ components: remoteAddr: type: string state: - type: string - enum: [idle, read, publish] + $ref: '#/components/schemas/SRTConnState' path: type: string query: @@ -1344,7 +1468,7 @@ components: type: object properties: codec: - type: string + $ref: '#/components/schemas/AlwaysAvailableTrackCodec' sampleRate: type: integer format: int64 @@ -1383,8 +1507,7 @@ components: remoteCandidate: type: string state: - type: string - enum: [read, publish] + $ref: '#/components/schemas/WebRTCSessionState' path: type: string query: diff --git a/internal/api/api.go b/internal/api/api.go index a1b2dcbd..2cb8a1a2 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -223,13 +223,13 @@ func (a *API) writeError(ctx *gin.Context, status int, err error) { // add error to response ctx.JSON(status, &defs.APIError{ - Status: "error", + Status: defs.APIErrorStatusError, Error: err.Error(), }) } func (a *API) writeOK(ctx *gin.Context) { - ctx.JSON(http.StatusOK, &defs.APIOK{Status: "ok"}) + ctx.JSON(http.StatusOK, &defs.APIOK{Status: defs.APIOKStatusOK}) } func (a *API) middlewarePreflightRequests(ctx *gin.Context) { @@ -255,7 +255,7 @@ func (a *API) middlewareAuth(ctx *gin.Context) { if err.AskCredentials { ctx.Header("WWW-Authenticate", `Basic realm="mediamtx"`) ctx.AbortWithStatusJSON(http.StatusUnauthorized, &defs.APIError{ - Status: "error", + Status: defs.APIErrorStatusError, Error: "authentication error", }) return @@ -267,7 +267,7 @@ func (a *API) middlewareAuth(ctx *gin.Context) { <-time.After(auth.PauseAfterError) ctx.AbortWithStatusJSON(http.StatusUnauthorized, &defs.APIError{ - Status: "error", + Status: defs.APIErrorStatusError, Error: "authentication error", }) return diff --git a/internal/api/api_paths_test.go b/internal/api/api_paths_test.go index 61a3683f..122e3826 100644 --- a/internal/api/api_paths_test.go +++ b/internal/api/api_paths_test.go @@ -38,21 +38,21 @@ func TestPathsList(t *testing.T) { "test1": { Name: "test1", ConfName: "test1", - Source: &defs.APIPathSource{Type: "publisher", ID: "pub1"}, + Source: &defs.APIPathSource{Type: defs.APIPathSourceTypeRTSPSession, ID: "pub1"}, Ready: true, ReadyTime: &now, - Tracks: []string{"H264", "Opus"}, + Tracks: []defs.APIPathTrackCodec{defs.APIPathTrackCodecH264, defs.APIPathTrackCodecOpus}, BytesReceived: 1000, BytesSent: 2000, Readers: []defs.APIPathReader{ - {Type: "reader", ID: "reader1"}, + {Type: defs.APIPathReaderTypeRTSPSession, ID: "reader1"}, }, }, "test2": { Name: "test2", ConfName: "test2", Ready: false, - Tracks: []string{}, + Tracks: []defs.APIPathTrackCodec{}, BytesReceived: 500, BytesSent: 100, Readers: []defs.APIPathReader{}, @@ -91,15 +91,15 @@ func TestPathsGet(t *testing.T) { "mystream": { Name: "mystream", ConfName: "mystream", - Source: &defs.APIPathSource{Type: "rtspSession", ID: "session123"}, + Source: &defs.APIPathSource{Type: defs.APIPathSourceTypeRTSPSession, ID: "session123"}, Ready: true, ReadyTime: &now, - Tracks: []string{"H264", "Opus"}, + Tracks: []defs.APIPathTrackCodec{defs.APIPathTrackCodecH264, defs.APIPathTrackCodecOpus}, BytesReceived: 123456, BytesSent: 789012, Readers: []defs.APIPathReader{ - {Type: "hlsMuxer", ID: "muxer1"}, - {Type: "webRTCSession", ID: "session456"}, + {Type: defs.APIPathReaderTypeHLSMuxer, ID: "muxer1"}, + {Type: defs.APIPathReaderTypeWebRTCSession, ID: "session456"}, }, }, }, @@ -128,7 +128,7 @@ func TestPathsGet(t *testing.T) { require.Equal(t, "mystream", out.ConfName) require.True(t, out.Ready) require.NotNil(t, out.Source) - require.Equal(t, "rtspSession", out.Source.Type) + require.Equal(t, defs.APIPathSourceTypeRTSPSession, out.Source.Type) require.Len(t, out.Tracks, 2) require.Len(t, out.Readers, 2) require.Equal(t, uint64(123456), out.BytesReceived) diff --git a/internal/conf/always_available_track.go b/internal/conf/always_available_track.go index 29efc64c..ce04ffbd 100644 --- a/internal/conf/always_available_track.go +++ b/internal/conf/always_available_track.go @@ -6,32 +6,12 @@ import ( "github.com/bluenviron/mediamtx/internal/conf/jsonwrapper" ) -// Codec is a codec of AlwaysAvailableTrack. -type Codec string - -// available codecs. -const ( - CodecAV1 Codec = "AV1" - CodecVP9 Codec = "VP9" - CodecH265 Codec = "H265" - CodecH264 Codec = "H264" - CodecMPEG4Audio Codec = "MPEG4Audio" - CodecOpus Codec = "Opus" - CodecG711 Codec = "G711" - CodecLPCM Codec = "LPCM" -) - -// UnmarshalEnv implements env.Unmarshaler. -func (d *Codec) UnmarshalEnv(_ string, v string) error { - return jsonwrapper.Unmarshal([]byte(`"`+v+`"`), d) -} - // AlwaysAvailableTrack is an item of alwaysAvailableTracks. type AlwaysAvailableTrack struct { - Codec Codec `json:"codec"` - SampleRate int `json:"sampleRate"` - ChannelCount int `json:"channelCount"` - MULaw bool `json:"muLaw"` + Codec AlwaysAvailableTrackCodec `json:"codec"` + SampleRate int `json:"sampleRate"` + ChannelCount int `json:"channelCount"` + MULaw bool `json:"muLaw"` } // UnmarshalJSON implements json.Unmarshaler. diff --git a/internal/conf/always_available_track_codec.go b/internal/conf/always_available_track_codec.go new file mode 100644 index 00000000..b40d0263 --- /dev/null +++ b/internal/conf/always_available_track_codec.go @@ -0,0 +1,23 @@ +package conf + +import "github.com/bluenviron/mediamtx/internal/conf/jsonwrapper" + +// AlwaysAvailableTrackCodec is a codec of AlwaysAvailableTrack. +type AlwaysAvailableTrackCodec string + +// available codecs. +const ( + CodecAV1 AlwaysAvailableTrackCodec = "AV1" + CodecVP9 AlwaysAvailableTrackCodec = "VP9" + CodecH265 AlwaysAvailableTrackCodec = "H265" + CodecH264 AlwaysAvailableTrackCodec = "H264" + CodecMPEG4Audio AlwaysAvailableTrackCodec = "MPEG4Audio" + CodecOpus AlwaysAvailableTrackCodec = "Opus" + CodecG711 AlwaysAvailableTrackCodec = "G711" + CodecLPCM AlwaysAvailableTrackCodec = "LPCM" +) + +// UnmarshalEnv implements env.Unmarshaler. +func (d *AlwaysAvailableTrackCodec) UnmarshalEnv(_ string, v string) error { + return jsonwrapper.Unmarshal([]byte(`"`+v+`"`), d) +} diff --git a/internal/core/api_test.go b/internal/core/api_test.go index 72acf564..e8f58041 100644 --- a/internal/core/api_test.go +++ b/internal/core/api_test.go @@ -26,6 +26,7 @@ import ( pwebrtc "github.com/pion/webrtc/v4" "github.com/stretchr/testify/require" + "github.com/bluenviron/mediamtx/internal/defs" "github.com/bluenviron/mediamtx/internal/protocols/webrtc" "github.com/bluenviron/mediamtx/internal/protocols/whip" "github.com/bluenviron/mediamtx/internal/test" @@ -79,12 +80,12 @@ func TestAPIPathsList(t *testing.T) { } type path struct { - Name string `json:"name"` - Source pathSource `json:"source"` - Ready bool `json:"ready"` - Tracks []string `json:"tracks"` - BytesReceived uint64 `json:"bytesReceived"` - BytesSent uint64 `json:"bytesSent"` + Name string `json:"name"` + Source pathSource `json:"source"` + Ready bool `json:"ready"` + Tracks []defs.APIPathTrackCodec `json:"tracks"` + BytesReceived uint64 `json:"bytesReceived"` + BytesSent uint64 `json:"bytesSent"` } type pathList struct { @@ -136,7 +137,7 @@ func TestAPIPathsList(t *testing.T) { Type: "rtspSession", }, Ready: true, - Tracks: []string{"H264", "MPEG-4 Audio"}, + Tracks: []defs.APIPathTrackCodec{defs.APIPathTrackCodecH264, defs.APIPathTrackCodecMPEG4Audio}, BytesReceived: 17, }}, }, out) @@ -184,7 +185,7 @@ func TestAPIPathsList(t *testing.T) { Type: "rtspsSession", }, Ready: true, - Tracks: []string{"H264", "MPEG-4 Audio"}, + Tracks: []defs.APIPathTrackCodec{defs.APIPathTrackCodecH264, defs.APIPathTrackCodecMPEG4Audio}, }}, }, out) }) @@ -213,7 +214,7 @@ func TestAPIPathsList(t *testing.T) { Type: "rtspSource", }, Ready: false, - Tracks: []string{}, + Tracks: []defs.APIPathTrackCodec{}, }}, }, out) }) @@ -242,7 +243,7 @@ func TestAPIPathsList(t *testing.T) { Type: "rtmpSource", }, Ready: false, - Tracks: []string{}, + Tracks: []defs.APIPathTrackCodec{}, }}, }, out) }) @@ -271,7 +272,7 @@ func TestAPIPathsList(t *testing.T) { Type: "hlsSource", }, Ready: false, - Tracks: []string{}, + Tracks: []defs.APIPathTrackCodec{}, }}, }, out) }) @@ -295,12 +296,12 @@ func TestAPIPathsGet(t *testing.T) { } type path struct { - Name string `json:"name"` - Source pathSource `json:"source"` - Ready bool `json:"Ready"` - Tracks []string `json:"tracks"` - BytesReceived uint64 `json:"bytesReceived"` - BytesSent uint64 `json:"bytesSent"` + Name string `json:"name"` + Source pathSource `json:"source"` + Ready bool `json:"Ready"` + Tracks []defs.APIPathTrackCodec `json:"tracks"` + BytesReceived uint64 `json:"bytesReceived"` + BytesSent uint64 `json:"bytesSent"` } var pathName string @@ -329,7 +330,7 @@ func TestAPIPathsGet(t *testing.T) { Type: "rtspSession", }, Ready: true, - Tracks: []string{"H264"}, + Tracks: []defs.APIPathTrackCodec{defs.APIPathTrackCodecH264}, }, out) } else { res, err := hc.Get("http://localhost:9997/v3/paths/get/" + pathName) diff --git a/internal/core/path.go b/internal/core/path.go index e5a77afc..e36baf7b 100644 --- a/internal/core/path.go +++ b/internal/core/path.go @@ -655,9 +655,9 @@ func (pa *path) doAPIPathsGet(req pathAPIPathsGetReq) { v := pa.source.APISourceDescribe() return v }(), - Tracks: func() []string { + Tracks: func() []defs.APIPathTrackCodec { if !pa.isAvailable() { - return []string{} + return []defs.APIPathTrackCodec{} } return defs.MediasToCodecs(pa.stream.Desc.Medias) }(), diff --git a/internal/core/source_redirect.go b/internal/core/source_redirect.go index 296cf4fd..cb1f6931 100644 --- a/internal/core/source_redirect.go +++ b/internal/core/source_redirect.go @@ -14,7 +14,7 @@ func (*sourceRedirect) Log(logger.Level, string, ...any) { // APISourceDescribe implements source. func (*sourceRedirect) APISourceDescribe() *defs.APIPathSource { return &defs.APIPathSource{ - Type: "redirect", + Type: defs.APIPathSourceTypeRedirect, ID: "", } } diff --git a/internal/defs/api.go b/internal/defs/api.go index 35294d3c..b66e9e30 100644 --- a/internal/defs/api.go +++ b/internal/defs/api.go @@ -3,62 +3,34 @@ package defs import ( "time" - "github.com/google/uuid" - "github.com/bluenviron/mediamtx/internal/conf" ) -// APIPathManager contains methods used by the API and Metrics server. -type APIPathManager interface { - APIPathsList() (*APIPathList, error) - APIPathsGet(string) (*APIPath, error) -} +// APIOKStatus is the status of a successful response. +type APIOKStatus string -// APIHLSServer contains methods used by the API and Metrics server. -type APIHLSServer interface { - APIMuxersList() (*APIHLSMuxerList, error) - APIMuxersGet(string) (*APIHLSMuxer, error) -} +// statuses. +const ( + APIOKStatusOK APIOKStatus = "ok" +) -// APIRTSPServer contains methods used by the API and Metrics server. -type APIRTSPServer interface { - APIConnsList() (*APIRTSPConnsList, error) - APIConnsGet(uuid.UUID) (*APIRTSPConn, error) - APISessionsList() (*APIRTSPSessionList, error) - APISessionsGet(uuid.UUID) (*APIRTSPSession, error) - APISessionsKick(uuid.UUID) error -} +// APIErrorStatus is the status of an error response. +type APIErrorStatus string -// APIRTMPServer contains methods used by the API and Metrics server. -type APIRTMPServer interface { - APIConnsList() (*APIRTMPConnList, error) - APIConnsGet(uuid.UUID) (*APIRTMPConn, error) - APIConnsKick(uuid.UUID) error -} - -// APISRTServer contains methods used by the API and Metrics server. -type APISRTServer interface { - APIConnsList() (*APISRTConnList, error) - APIConnsGet(uuid.UUID) (*APISRTConn, error) - APIConnsKick(uuid.UUID) error -} - -// APIWebRTCServer contains methods used by the API and Metrics server. -type APIWebRTCServer interface { - APISessionsList() (*APIWebRTCSessionList, error) - APISessionsGet(uuid.UUID) (*APIWebRTCSession, error) - APISessionsKick(uuid.UUID) error -} +// statuses. +const ( + APIErrorStatusError APIErrorStatus = "error" +) // APIOK is returned on success. type APIOK struct { - Status string `json:"status"` + Status APIOKStatus `json:"status"` } // APIError is a generic error. type APIError struct { - Status string `json:"status"` - Error string `json:"error"` + Status APIErrorStatus `json:"status"` + Error string `json:"error"` } // APIInfo is a info response. @@ -73,348 +45,3 @@ type APIPathConfList struct { PageCount int `json:"pageCount"` Items []conf.Path `json:"items"` } - -// APIPathSource is a source. -type APIPathSource struct { - Type string `json:"type"` - ID string `json:"id"` -} - -// APIPathReader is a reader. -type APIPathReader struct { - Type string `json:"type"` - ID string `json:"id"` -} - -// APIPath is a path. -type APIPath struct { - Name string `json:"name"` - ConfName string `json:"confName"` - Ready bool `json:"ready" deprecated:"true"` - ReadyTime *time.Time `json:"readyTime" deprecated:"true"` - Available bool `json:"available"` - AvailableTime *time.Time `json:"availableTime"` - Online bool `json:"online"` - OnlineTime *time.Time `json:"onlineTime"` - Source *APIPathSource `json:"source"` - Tracks []string `json:"tracks"` - BytesReceived uint64 `json:"bytesReceived"` - BytesSent uint64 `json:"bytesSent"` - Readers []APIPathReader `json:"readers"` -} - -// APIPathList is a list of paths. -type APIPathList struct { - ItemCount int `json:"itemCount"` - PageCount int `json:"pageCount"` - Items []APIPath `json:"items"` -} - -// APIHLSMuxer is an HLS muxer. -type APIHLSMuxer struct { - Path string `json:"path"` - Created time.Time `json:"created"` - LastRequest time.Time `json:"lastRequest"` - BytesSent uint64 `json:"bytesSent"` -} - -// APIHLSMuxerList is a list of HLS muxers. -type APIHLSMuxerList struct { - ItemCount int `json:"itemCount"` - PageCount int `json:"pageCount"` - Items []APIHLSMuxer `json:"items"` -} - -// APIRTMPConnState is the state of a RTMP connection. -type APIRTMPConnState string - -// states. -const ( - APIRTMPConnStateIdle APIRTMPConnState = "idle" - APIRTMPConnStateRead APIRTMPConnState = "read" - APIRTMPConnStatePublish APIRTMPConnState = "publish" -) - -// APIRTMPConn is a RTMP connection. -type APIRTMPConn struct { - ID uuid.UUID `json:"id"` - Created time.Time `json:"created"` - RemoteAddr string `json:"remoteAddr"` - State APIRTMPConnState `json:"state"` - Path string `json:"path"` - Query string `json:"query"` - User string `json:"user"` - BytesReceived uint64 `json:"bytesReceived"` - BytesSent uint64 `json:"bytesSent"` -} - -// APIRTMPConnList is a list of RTMP connections. -type APIRTMPConnList struct { - ItemCount int `json:"itemCount"` - PageCount int `json:"pageCount"` - Items []APIRTMPConn `json:"items"` -} - -// APIRTSPConn is a RTSP connection. -type APIRTSPConn struct { - ID uuid.UUID `json:"id"` - Created time.Time `json:"created"` - RemoteAddr string `json:"remoteAddr"` - Session *uuid.UUID `json:"session"` - Tunnel string `json:"tunnel"` - BytesReceived uint64 `json:"bytesReceived"` - BytesSent uint64 `json:"bytesSent"` -} - -// APIRTSPConnsList is a list of RTSP connections. -type APIRTSPConnsList struct { - ItemCount int `json:"itemCount"` - PageCount int `json:"pageCount"` - Items []APIRTSPConn `json:"items"` -} - -// APIRTSPSessionState is the state of a RTSP session. -type APIRTSPSessionState string - -// states. -const ( - APIRTSPSessionStateIdle APIRTSPSessionState = "idle" - APIRTSPSessionStateRead APIRTSPSessionState = "read" - APIRTSPSessionStatePublish APIRTSPSessionState = "publish" -) - -// APIRTSPSession is a RTSP session. -type APIRTSPSession struct { - ID uuid.UUID `json:"id"` - Created time.Time `json:"created"` - RemoteAddr string `json:"remoteAddr"` - State APIRTSPSessionState `json:"state"` - Path string `json:"path"` - Query string `json:"query"` - User string `json:"user"` - Transport *string `json:"transport"` - Profile *string `json:"profile"` - Conns []uuid.UUID `json:"conns"` - BytesReceived uint64 `json:"bytesReceived"` - BytesSent uint64 `json:"bytesSent"` - RTPPacketsReceived uint64 `json:"rtpPacketsReceived"` - RTPPacketsSent uint64 `json:"rtpPacketsSent"` - RTPPacketsLost uint64 `json:"rtpPacketsLost"` - RTPPacketsInError uint64 `json:"rtpPacketsInError"` - RTPPacketsJitter float64 `json:"rtpPacketsJitter"` - RTCPPacketsReceived uint64 `json:"rtcpPacketsReceived"` - RTCPPacketsSent uint64 `json:"rtcpPacketsSent"` - RTCPPacketsInError uint64 `json:"rtcpPacketsInError"` -} - -// APIRTSPSessionList is a list of RTSP sessions. -type APIRTSPSessionList struct { - ItemCount int `json:"itemCount"` - PageCount int `json:"pageCount"` - Items []APIRTSPSession `json:"items"` -} - -// APISRTConnState is the state of a SRT connection. -type APISRTConnState string - -// states. -const ( - APISRTConnStateIdle APISRTConnState = "idle" - APISRTConnStateRead APISRTConnState = "read" - APISRTConnStatePublish APISRTConnState = "publish" -) - -// APISRTConn is a SRT connection. -type APISRTConn struct { - ID uuid.UUID `json:"id"` - Created time.Time `json:"created"` - RemoteAddr string `json:"remoteAddr"` - State APISRTConnState `json:"state"` - Path string `json:"path"` - Query string `json:"query"` - User string `json:"user"` - - // The metric names/comments are pulled from GoSRT - - // The total number of sent DATA packets, including retransmitted packets - PacketsSent uint64 `json:"packetsSent"` - // The total number of received DATA packets, including retransmitted packets - PacketsReceived uint64 `json:"packetsReceived"` - // The total number of unique DATA packets sent by the SRT sender - PacketsSentUnique uint64 `json:"packetsSentUnique"` - // The total number of unique original, retransmitted or recovered by the packet filter DATA packets - // received in time, decrypted without errors and, as a result, scheduled for delivery to the - // upstream application by the SRT receiver. - PacketsReceivedUnique uint64 `json:"packetsReceivedUnique"` - // The total number of data packets considered or reported as lost at the sender side. - // Does not correspond to the packets detected as lost at the receiver side. - PacketsSendLoss uint64 `json:"packetsSendLoss"` - // The total number of SRT DATA packets detected as presently missing (either reordered or lost) at the receiver side - PacketsReceivedLoss uint64 `json:"packetsReceivedLoss"` - // The total number of retransmitted packets sent by the SRT sender - PacketsRetrans uint64 `json:"packetsRetrans"` - // The total number of retransmitted packets registered at the receiver side - PacketsReceivedRetrans uint64 `json:"packetsReceivedRetrans"` - // The total number of sent ACK (Acknowledgement) control packets - PacketsSentACK uint64 `json:"packetsSentACK"` - // The total number of received ACK (Acknowledgement) control packets - PacketsReceivedACK uint64 `json:"packetsReceivedACK"` - // The total number of sent NAK (Negative Acknowledgement) control packets - PacketsSentNAK uint64 `json:"packetsSentNAK"` - // The total number of received NAK (Negative Acknowledgement) control packets - PacketsReceivedNAK uint64 `json:"packetsReceivedNAK"` - // The total number of sent KM (Key Material) control packets - PacketsSentKM uint64 `json:"packetsSentKM"` - // The total number of received KM (Key Material) control packets - PacketsReceivedKM uint64 `json:"packetsReceivedKM"` - // The total accumulated time in microseconds, during which the SRT sender has some data to transmit, - // including packets that have been sent, but not yet acknowledged - UsSndDuration uint64 `json:"usSndDuration"` - // ?? - PacketsReceivedBelated uint64 `json:"packetsReceivedBelated"` - // The total number of dropped by the SRT sender DATA packets that have no chance to be delivered in time - PacketsSendDrop uint64 `json:"packetsSendDrop"` - // The total number of dropped by the SRT receiver and, as a result, - // not delivered to the upstream application DATA packets - PacketsReceivedDrop uint64 `json:"packetsReceivedDrop"` - // The total number of packets that failed to be decrypted at the receiver side - PacketsReceivedUndecrypt uint64 `json:"packetsReceivedUndecrypt"` - - // Same as packetsReceived, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) - BytesReceived uint64 `json:"bytesReceived"` - // Same as packetsSent, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) - BytesSent uint64 `json:"bytesSent"` - // Same as packetsSentUnique, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) - BytesSentUnique uint64 `json:"bytesSentUnique"` - // Same as packetsReceivedUnique, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) - BytesReceivedUnique uint64 `json:"bytesReceivedUnique"` - // Same as packetsReceivedLoss, but expressed in bytes, including payload and all the headers (IP, TCP, SRT), - // bytes for the presently missing (either reordered or lost) packets' payloads are estimated - // based on the average packet size - BytesReceivedLoss uint64 `json:"bytesReceivedLoss"` - // Same as packetsRetrans, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) - BytesRetrans uint64 `json:"bytesRetrans"` - // Same as packetsReceivedRetrans, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) - BytesReceivedRetrans uint64 `json:"bytesReceivedRetrans"` - // Same as PacketsReceivedBelated, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) - BytesReceivedBelated uint64 `json:"bytesReceivedBelated"` - // Same as packetsSendDrop, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) - BytesSendDrop uint64 `json:"bytesSendDrop"` - // Same as packetsReceivedDrop, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) - BytesReceivedDrop uint64 `json:"bytesReceivedDrop"` - // Same as packetsReceivedUndecrypt, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) - BytesReceivedUndecrypt uint64 `json:"bytesReceivedUndecrypt"` - - // Current minimum time interval between which consecutive packets are sent, in microseconds - UsPacketsSendPeriod float64 `json:"usPacketsSendPeriod"` - // The maximum number of packets that can be "in flight" - PacketsFlowWindow uint64 `json:"packetsFlowWindow"` - // The number of packets in flight - PacketsFlightSize uint64 `json:"packetsFlightSize"` - // Smoothed round-trip time (SRTT), an exponentially-weighted moving average (EWMA) - // of an endpoint's RTT samples, in milliseconds - MsRTT float64 `json:"msRTT"` - // Current transmission bandwidth, in Mbps - MbpsSendRate float64 `json:"mbpsSendRate"` - // Current receiving bandwidth, in Mbps - MbpsReceiveRate float64 `json:"mbpsReceiveRate"` - // Estimated capacity of the network link, in Mbps - MbpsLinkCapacity float64 `json:"mbpsLinkCapacity"` - // The available space in the sender's buffer, in bytes - BytesAvailSendBuf uint64 `json:"bytesAvailSendBuf"` - // The available space in the receiver's buffer, in bytes - BytesAvailReceiveBuf uint64 `json:"bytesAvailReceiveBuf"` - // Transmission bandwidth limit, in Mbps - MbpsMaxBW float64 `json:"mbpsMaxBW"` - // Maximum Segment Size (MSS), in bytes - ByteMSS uint64 `json:"byteMSS"` - // The number of packets in the sender's buffer that are already scheduled - // for sending or even possibly sent, but not yet acknowledged - PacketsSendBuf uint64 `json:"packetsSendBuf"` - // Instantaneous (current) value of packetsSndBuf, but expressed in bytes, - // including payload and all headers (IP, TCP, SRT) - BytesSendBuf uint64 `json:"bytesSendBuf"` - // The timespan (msec) of packets in the sender's buffer (unacknowledged packets) - MsSendBuf uint64 `json:"msSendBuf"` - // Timestamp-based Packet Delivery Delay value of the peer - MsSendTsbPdDelay uint64 `json:"msSendTsbPdDelay"` - // The number of acknowledged packets in receiver's buffer - PacketsReceiveBuf uint64 `json:"packetsReceiveBuf"` - // Instantaneous (current) value of packetsRcvBuf, expressed in bytes, including payload and all headers (IP, TCP, SRT) - BytesReceiveBuf uint64 `json:"bytesReceiveBuf"` - // The timespan (msec) of acknowledged packets in the receiver's buffer - MsReceiveBuf uint64 `json:"msReceiveBuf"` - // Timestamp-based Packet Delivery Delay value set on the socket via SRTO_RCVLATENCY or SRTO_LATENCY - MsReceiveTsbPdDelay uint64 `json:"msReceiveTsbPdDelay"` - // Instant value of the packet reorder tolerance - PacketsReorderTolerance uint64 `json:"packetsReorderTolerance"` - // Accumulated difference between the current time and the time-to-play of a packet that is received late - PacketsReceivedAvgBelatedTime uint64 `json:"packetsReceivedAvgBelatedTime"` - // Percentage of resent data vs. sent data - PacketsSendLossRate float64 `json:"packetsSendLossRate"` - // Percentage of retransmitted data vs. received data - PacketsReceivedLossRate float64 `json:"packetsReceivedLossRate"` -} - -// APISRTConnList is a list of SRT connections. -type APISRTConnList struct { - ItemCount int `json:"itemCount"` - PageCount int `json:"pageCount"` - Items []APISRTConn `json:"items"` -} - -// APIWebRTCSessionState is the state of a WebRTC connection. -type APIWebRTCSessionState string - -// states. -const ( - APIWebRTCSessionStateRead APIWebRTCSessionState = "read" - APIWebRTCSessionStatePublish APIWebRTCSessionState = "publish" -) - -// APIWebRTCSession is a WebRTC session. -type APIWebRTCSession struct { - ID uuid.UUID `json:"id"` - Created time.Time `json:"created"` - RemoteAddr string `json:"remoteAddr"` - PeerConnectionEstablished bool `json:"peerConnectionEstablished"` - LocalCandidate string `json:"localCandidate"` - RemoteCandidate string `json:"remoteCandidate"` - State APIWebRTCSessionState `json:"state"` - Path string `json:"path"` - Query string `json:"query"` - User string `json:"user"` - BytesReceived uint64 `json:"bytesReceived"` - BytesSent uint64 `json:"bytesSent"` - RTPPacketsReceived uint64 `json:"rtpPacketsReceived"` - RTPPacketsSent uint64 `json:"rtpPacketsSent"` - RTPPacketsLost uint64 `json:"rtpPacketsLost"` - RTPPacketsJitter float64 `json:"rtpPacketsJitter"` - RTCPPacketsReceived uint64 `json:"rtcpPacketsReceived"` - RTCPPacketsSent uint64 `json:"rtcpPacketsSent"` -} - -// APIWebRTCSessionList is a list of WebRTC sessions. -type APIWebRTCSessionList struct { - ItemCount int `json:"itemCount"` - PageCount int `json:"pageCount"` - Items []APIWebRTCSession `json:"items"` -} - -// APIRecordingSegment is a recording segment. -type APIRecordingSegment struct { - Start time.Time `json:"start"` -} - -// APIRecording is a recording. -type APIRecording struct { - Name string `json:"name"` - Segments []APIRecordingSegment `json:"segments"` -} - -// APIRecordingList is a list of recordings. -type APIRecordingList struct { - ItemCount int `json:"itemCount"` - PageCount int `json:"pageCount"` - Items []APIRecording `json:"items"` -} diff --git a/internal/defs/api_hls.go b/internal/defs/api_hls.go new file mode 100644 index 00000000..0b3f0806 --- /dev/null +++ b/internal/defs/api_hls.go @@ -0,0 +1,24 @@ +package defs + +import "time" + +// APIHLSServer contains methods used by the API and Metrics server. +type APIHLSServer interface { + APIMuxersList() (*APIHLSMuxerList, error) + APIMuxersGet(string) (*APIHLSMuxer, error) +} + +// APIHLSMuxer is an HLS muxer. +type APIHLSMuxer struct { + Path string `json:"path"` + Created time.Time `json:"created"` + LastRequest time.Time `json:"lastRequest"` + BytesSent uint64 `json:"bytesSent"` +} + +// APIHLSMuxerList is a list of HLS muxers. +type APIHLSMuxerList struct { + ItemCount int `json:"itemCount"` + PageCount int `json:"pageCount"` + Items []APIHLSMuxer `json:"items"` +} diff --git a/internal/defs/api_path.go b/internal/defs/api_path.go new file mode 100644 index 00000000..096f440b --- /dev/null +++ b/internal/defs/api_path.go @@ -0,0 +1,86 @@ +package defs + +import ( + "time" +) + +// APIPathManager contains methods used by the API and Metrics server. +type APIPathManager interface { + APIPathsList() (*APIPathList, error) + APIPathsGet(string) (*APIPath, error) +} + +// APIPathSourceType is the type of a path source. +type APIPathSourceType string + +// source types. +const ( + APIPathSourceTypeHLSSource APIPathSourceType = "hlsSource" + APIPathSourceTypeRedirect APIPathSourceType = "redirect" + APIPathSourceTypeRPICameraSource APIPathSourceType = "rpiCameraSource" + APIPathSourceTypeRTMPConn APIPathSourceType = "rtmpConn" + APIPathSourceTypeRTMPSConn APIPathSourceType = "rtmpsConn" + APIPathSourceTypeRTMPSource APIPathSourceType = "rtmpSource" + APIPathSourceTypeRTSPSession APIPathSourceType = "rtspSession" + APIPathSourceTypeRTSPSource APIPathSourceType = "rtspSource" + APIPathSourceTypeRTSPSSession APIPathSourceType = "rtspsSession" + APIPathSourceTypeSRTConn APIPathSourceType = "srtConn" + APIPathSourceTypeSRTSource APIPathSourceType = "srtSource" + APIPathSourceTypeMPEGTSSource APIPathSourceType = "mpegtsSource" + APIPathSourceTypeRTPSource APIPathSourceType = "rtpSource" + APIPathSourceTypeWebRTCSession APIPathSourceType = "webRTCSession" + APIPathSourceTypeWebRTCSource APIPathSourceType = "webRTCSource" +) + +// APIPathSource is a source. +type APIPathSource struct { + Type APIPathSourceType `json:"type"` + ID string `json:"id"` +} + +// APIPathReaderType is the type of a path reader. +type APIPathReaderType string + +// reader types. +const ( + APIPathReaderTypeHLSMuxer APIPathReaderType = "hlsMuxer" + APIPathReaderTypeRTMPConn APIPathReaderType = "rtmpConn" + APIPathReaderTypeRTMPSConn APIPathReaderType = "rtmpsConn" + APIPathReaderTypeRTSPConn APIPathReaderType = "rtspConn" + APIPathReaderTypeRPICameraSecondary APIPathReaderType = "rpiCameraSecondary" + APIPathReaderTypeRTSPSession APIPathReaderType = "rtspSession" + APIPathReaderTypeRTSPSConn APIPathReaderType = "rtspsConn" + APIPathReaderTypeRTSPSSession APIPathReaderType = "rtspsSession" + APIPathReaderTypeSRTConn APIPathReaderType = "srtConn" + APIPathReaderTypeWebRTCSession APIPathReaderType = "webRTCSession" +) + +// APIPathReader is a reader. +type APIPathReader struct { + Type APIPathReaderType `json:"type"` + ID string `json:"id"` +} + +// APIPath is a path. +type APIPath struct { + Name string `json:"name"` + ConfName string `json:"confName"` + Ready bool `json:"ready" deprecated:"true"` + ReadyTime *time.Time `json:"readyTime" deprecated:"true"` + Available bool `json:"available"` + AvailableTime *time.Time `json:"availableTime"` + Online bool `json:"online"` + OnlineTime *time.Time `json:"onlineTime"` + Source *APIPathSource `json:"source"` + Tracks []APIPathTrackCodec `json:"tracks"` + BytesReceived uint64 `json:"bytesReceived"` + BytesSent uint64 `json:"bytesSent"` + Readers []APIPathReader `json:"readers"` +} + +// APIPathList is a list of paths. +type APIPathList struct { + ItemCount int `json:"itemCount"` + PageCount int `json:"pageCount"` + Items []APIPath `json:"items"` +} diff --git a/internal/defs/api_path_track_codec.go b/internal/defs/api_path_track_codec.go new file mode 100644 index 00000000..df670840 --- /dev/null +++ b/internal/defs/api_path_track_codec.go @@ -0,0 +1,122 @@ +package defs + +import ( + "github.com/bluenviron/gortsplib/v5/pkg/description" + "github.com/bluenviron/gortsplib/v5/pkg/format" +) + +// APIPathTrackCodec is a path track codec. +type APIPathTrackCodec string + +// path track codecs. +const ( + // video + APIPathTrackCodecAV1 APIPathTrackCodec = "AV1" + APIPathTrackCodecVP9 APIPathTrackCodec = "VP9" + APIPathTrackCodecVP8 APIPathTrackCodec = "VP8" + APIPathTrackCodecH265 APIPathTrackCodec = "H265" + APIPathTrackCodecH264 APIPathTrackCodec = "H264" + APIPathTrackCodecMPEG4Video APIPathTrackCodec = "MPEG-4 Video" + APIPathTrackCodecMPEG1Video APIPathTrackCodec = "MPEG-1 Video" + APIPathTrackCodecMJPEG APIPathTrackCodec = "MJPEG" + // audio + APIPathTrackCodecOpus APIPathTrackCodec = "Opus" + APIPathTrackCodecVorbis APIPathTrackCodec = "Vorbis" + APIPathTrackCodecMPEG4Audio APIPathTrackCodec = "MPEG-4 Audio" + APIPathTrackCodecMPEG4AudioLATM APIPathTrackCodec = "MPEG-4 Audio LATM" + APIPathTrackCodecMPEG1Audio APIPathTrackCodec = "MPEG-1 Audio" + APIPathTrackCodecAC3 APIPathTrackCodec = "AC3" + APIPathTrackCodecSpeex APIPathTrackCodec = "Speex" + APIPathTrackCodecG726 APIPathTrackCodec = "G726" + APIPathTrackCodecG722 APIPathTrackCodec = "G722" + APIPathTrackCodecG711 APIPathTrackCodec = "G711" + APIPathTrackCodecLPCM APIPathTrackCodec = "LPCM" + // other + APIPathTrackCodecMPEGTS APIPathTrackCodec = "MPEG-TS" + APIPathTrackCodecKLV APIPathTrackCodec = "KLV" + APIPathTrackCodecGeneric APIPathTrackCodec = "Generic" +) + +// FormatsToCodecs returns codecs of given formats. +func FormatsToCodecs(formats []format.Format) []APIPathTrackCodec { + ret := make([]APIPathTrackCodec, len(formats)) + + for i, forma := range formats { + switch forma.(type) { + // video + case *format.AV1: + ret[i] = APIPathTrackCodecAV1 + case *format.VP9: + ret[i] = APIPathTrackCodecVP9 + case *format.VP8: + ret[i] = APIPathTrackCodecVP8 + case *format.H265: + ret[i] = APIPathTrackCodecH265 + case *format.H264: + ret[i] = APIPathTrackCodecH264 + case *format.MPEG4Video: + ret[i] = APIPathTrackCodecMPEG4Video + case *format.MPEG1Video: + ret[i] = APIPathTrackCodecMPEG1Video + case *format.MJPEG: + ret[i] = APIPathTrackCodecMJPEG + // audio + case *format.Opus: + ret[i] = APIPathTrackCodecOpus + case *format.Vorbis: + ret[i] = APIPathTrackCodecVorbis + case *format.MPEG4Audio: + ret[i] = APIPathTrackCodecMPEG4Audio + case *format.MPEG4AudioLATM: + ret[i] = APIPathTrackCodecMPEG4AudioLATM + case *format.MPEG1Audio: + ret[i] = APIPathTrackCodecMPEG1Audio + case *format.AC3: + ret[i] = APIPathTrackCodecAC3 + case *format.Speex: + ret[i] = APIPathTrackCodecSpeex + case *format.G726: + ret[i] = APIPathTrackCodecG726 + case *format.G722: + ret[i] = APIPathTrackCodecG722 + case *format.G711: + ret[i] = APIPathTrackCodecG711 + case *format.LPCM: + ret[i] = APIPathTrackCodecLPCM + // other + case *format.MPEGTS: + ret[i] = APIPathTrackCodecMPEGTS + case *format.KLV: + ret[i] = APIPathTrackCodecKLV + default: + ret[i] = APIPathTrackCodecGeneric + } + } + + return ret +} + +func gatherFormats(medias []*description.Media) []format.Format { + n := 0 + for _, media := range medias { + n += len(media.Formats) + } + + if n == 0 { + return nil + } + + formats := make([]format.Format, n) + n = 0 + + for _, media := range medias { + n += copy(formats[n:], media.Formats) + } + + return formats +} + +// MediasToCodecs returns codecs of given medias. +func MediasToCodecs(medias []*description.Media) []APIPathTrackCodec { + return FormatsToCodecs(gatherFormats(medias)) +} diff --git a/internal/defs/api_path_track_codec_test.go b/internal/defs/api_path_track_codec_test.go new file mode 100644 index 00000000..529bc849 --- /dev/null +++ b/internal/defs/api_path_track_codec_test.go @@ -0,0 +1,60 @@ +package defs + +import ( + "testing" + + "github.com/bluenviron/gortsplib/v5/pkg/format" + "github.com/stretchr/testify/require" +) + +func TestFormatsToCodecs(t *testing.T) { + codecs := FormatsToCodecs([]format.Format{ + &format.AV1{}, + &format.VP9{}, + &format.VP8{}, + &format.H265{}, + &format.H264{}, + &format.MPEG4Video{}, + &format.MPEG1Video{}, + &format.MJPEG{}, + &format.Opus{}, + &format.Vorbis{}, + &format.MPEG4Audio{}, + &format.MPEG4AudioLATM{}, + &format.MPEG1Audio{}, + &format.AC3{}, + &format.Speex{}, + &format.G726{}, + &format.G722{}, + &format.G711{}, + &format.LPCM{}, + &format.MPEGTS{}, + &format.KLV{}, + &format.Generic{}, + }) + + require.Equal(t, []APIPathTrackCodec{ + APIPathTrackCodecAV1, + APIPathTrackCodecVP9, + APIPathTrackCodecVP8, + APIPathTrackCodecH265, + APIPathTrackCodecH264, + APIPathTrackCodecMPEG4Video, + APIPathTrackCodecMPEG1Video, + APIPathTrackCodecMJPEG, + APIPathTrackCodecOpus, + APIPathTrackCodecVorbis, + APIPathTrackCodecMPEG4Audio, + APIPathTrackCodecMPEG4AudioLATM, + APIPathTrackCodecMPEG1Audio, + APIPathTrackCodecAC3, + APIPathTrackCodecSpeex, + APIPathTrackCodecG726, + APIPathTrackCodecG722, + APIPathTrackCodecG711, + APIPathTrackCodecLPCM, + APIPathTrackCodecMPEGTS, + APIPathTrackCodecKLV, + APIPathTrackCodecGeneric, + }, codecs) +} diff --git a/internal/defs/api_recording.go b/internal/defs/api_recording.go new file mode 100644 index 00000000..634720db --- /dev/null +++ b/internal/defs/api_recording.go @@ -0,0 +1,21 @@ +package defs + +import "time" + +// APIRecordingSegment is a recording segment. +type APIRecordingSegment struct { + Start time.Time `json:"start"` +} + +// APIRecording is a recording. +type APIRecording struct { + Name string `json:"name"` + Segments []APIRecordingSegment `json:"segments"` +} + +// APIRecordingList is a list of recordings. +type APIRecordingList struct { + ItemCount int `json:"itemCount"` + PageCount int `json:"pageCount"` + Items []APIRecording `json:"items"` +} diff --git a/internal/defs/api_rtmp.go b/internal/defs/api_rtmp.go new file mode 100644 index 00000000..242f7b37 --- /dev/null +++ b/internal/defs/api_rtmp.go @@ -0,0 +1,44 @@ +package defs + +import ( + "time" + + "github.com/google/uuid" +) + +// APIRTMPServer contains methods used by the API and Metrics server. +type APIRTMPServer interface { + APIConnsList() (*APIRTMPConnList, error) + APIConnsGet(uuid.UUID) (*APIRTMPConn, error) + APIConnsKick(uuid.UUID) error +} + +// APIRTMPConnState is the state of a RTMP connection. +type APIRTMPConnState string + +// states. +const ( + APIRTMPConnStateIdle APIRTMPConnState = "idle" + APIRTMPConnStateRead APIRTMPConnState = "read" + APIRTMPConnStatePublish APIRTMPConnState = "publish" +) + +// APIRTMPConn is a RTMP connection. +type APIRTMPConn struct { + ID uuid.UUID `json:"id"` + Created time.Time `json:"created"` + RemoteAddr string `json:"remoteAddr"` + State APIRTMPConnState `json:"state"` + Path string `json:"path"` + Query string `json:"query"` + User string `json:"user"` + BytesReceived uint64 `json:"bytesReceived"` + BytesSent uint64 `json:"bytesSent"` +} + +// APIRTMPConnList is a list of RTMP connections. +type APIRTMPConnList struct { + ItemCount int `json:"itemCount"` + PageCount int `json:"pageCount"` + Items []APIRTMPConn `json:"items"` +} diff --git a/internal/defs/api_rtsp.go b/internal/defs/api_rtsp.go new file mode 100644 index 00000000..b78b7055 --- /dev/null +++ b/internal/defs/api_rtsp.go @@ -0,0 +1,75 @@ +package defs + +import ( + "time" + + "github.com/google/uuid" +) + +// APIRTSPServer contains methods used by the API and Metrics server. +type APIRTSPServer interface { + APIConnsList() (*APIRTSPConnsList, error) + APIConnsGet(uuid.UUID) (*APIRTSPConn, error) + APISessionsList() (*APIRTSPSessionList, error) + APISessionsGet(uuid.UUID) (*APIRTSPSession, error) + APISessionsKick(uuid.UUID) error +} + +// APIRTSPConn is a RTSP connection. +type APIRTSPConn struct { + ID uuid.UUID `json:"id"` + Created time.Time `json:"created"` + RemoteAddr string `json:"remoteAddr"` + Session *uuid.UUID `json:"session"` + Tunnel string `json:"tunnel"` + BytesReceived uint64 `json:"bytesReceived"` + BytesSent uint64 `json:"bytesSent"` +} + +// APIRTSPConnsList is a list of RTSP connections. +type APIRTSPConnsList struct { + ItemCount int `json:"itemCount"` + PageCount int `json:"pageCount"` + Items []APIRTSPConn `json:"items"` +} + +// APIRTSPSessionState is the state of a RTSP session. +type APIRTSPSessionState string + +// states. +const ( + APIRTSPSessionStateIdle APIRTSPSessionState = "idle" + APIRTSPSessionStateRead APIRTSPSessionState = "read" + APIRTSPSessionStatePublish APIRTSPSessionState = "publish" +) + +// APIRTSPSession is a RTSP session. +type APIRTSPSession struct { + ID uuid.UUID `json:"id"` + Created time.Time `json:"created"` + RemoteAddr string `json:"remoteAddr"` + State APIRTSPSessionState `json:"state"` + Path string `json:"path"` + Query string `json:"query"` + User string `json:"user"` + Transport *string `json:"transport"` + Profile *string `json:"profile"` + Conns []uuid.UUID `json:"conns"` + BytesReceived uint64 `json:"bytesReceived"` + BytesSent uint64 `json:"bytesSent"` + RTPPacketsReceived uint64 `json:"rtpPacketsReceived"` + RTPPacketsSent uint64 `json:"rtpPacketsSent"` + RTPPacketsLost uint64 `json:"rtpPacketsLost"` + RTPPacketsInError uint64 `json:"rtpPacketsInError"` + RTPPacketsJitter float64 `json:"rtpPacketsJitter"` + RTCPPacketsReceived uint64 `json:"rtcpPacketsReceived"` + RTCPPacketsSent uint64 `json:"rtcpPacketsSent"` + RTCPPacketsInError uint64 `json:"rtcpPacketsInError"` +} + +// APIRTSPSessionList is a list of RTSP sessions. +type APIRTSPSessionList struct { + ItemCount int `json:"itemCount"` + PageCount int `json:"pageCount"` + Items []APIRTSPSession `json:"items"` +} diff --git a/internal/defs/api_srt.go b/internal/defs/api_srt.go new file mode 100644 index 00000000..f6f094d1 --- /dev/null +++ b/internal/defs/api_srt.go @@ -0,0 +1,163 @@ +package defs + +import ( + "time" + + "github.com/google/uuid" +) + +// APISRTServer contains methods used by the API and Metrics server. +type APISRTServer interface { + APIConnsList() (*APISRTConnList, error) + APIConnsGet(uuid.UUID) (*APISRTConn, error) + APIConnsKick(uuid.UUID) error +} + +// APISRTConnState is the state of a SRT connection. +type APISRTConnState string + +// states. +const ( + APISRTConnStateIdle APISRTConnState = "idle" + APISRTConnStateRead APISRTConnState = "read" + APISRTConnStatePublish APISRTConnState = "publish" +) + +// APISRTConn is a SRT connection. +type APISRTConn struct { + ID uuid.UUID `json:"id"` + Created time.Time `json:"created"` + RemoteAddr string `json:"remoteAddr"` + State APISRTConnState `json:"state"` + Path string `json:"path"` + Query string `json:"query"` + User string `json:"user"` + + // The metric names/comments are pulled from GoSRT + + // The total number of sent DATA packets, including retransmitted packets + PacketsSent uint64 `json:"packetsSent"` + // The total number of received DATA packets, including retransmitted packets + PacketsReceived uint64 `json:"packetsReceived"` + // The total number of unique DATA packets sent by the SRT sender + PacketsSentUnique uint64 `json:"packetsSentUnique"` + // The total number of unique original, retransmitted or recovered by the packet filter DATA packets + // received in time, decrypted without errors and, as a result, scheduled for delivery to the + // upstream application by the SRT receiver. + PacketsReceivedUnique uint64 `json:"packetsReceivedUnique"` + // The total number of data packets considered or reported as lost at the sender side. + // Does not correspond to the packets detected as lost at the receiver side. + PacketsSendLoss uint64 `json:"packetsSendLoss"` + // The total number of SRT DATA packets detected as presently missing (either reordered or lost) at the receiver side + PacketsReceivedLoss uint64 `json:"packetsReceivedLoss"` + // The total number of retransmitted packets sent by the SRT sender + PacketsRetrans uint64 `json:"packetsRetrans"` + // The total number of retransmitted packets registered at the receiver side + PacketsReceivedRetrans uint64 `json:"packetsReceivedRetrans"` + // The total number of sent ACK (Acknowledgement) control packets + PacketsSentACK uint64 `json:"packetsSentACK"` + // The total number of received ACK (Acknowledgement) control packets + PacketsReceivedACK uint64 `json:"packetsReceivedACK"` + // The total number of sent NAK (Negative Acknowledgement) control packets + PacketsSentNAK uint64 `json:"packetsSentNAK"` + // The total number of received NAK (Negative Acknowledgement) control packets + PacketsReceivedNAK uint64 `json:"packetsReceivedNAK"` + // The total number of sent KM (Key Material) control packets + PacketsSentKM uint64 `json:"packetsSentKM"` + // The total number of received KM (Key Material) control packets + PacketsReceivedKM uint64 `json:"packetsReceivedKM"` + // The total accumulated time in microseconds, during which the SRT sender has some data to transmit, + // including packets that have been sent, but not yet acknowledged + UsSndDuration uint64 `json:"usSndDuration"` + // ?? + PacketsReceivedBelated uint64 `json:"packetsReceivedBelated"` + // The total number of dropped by the SRT sender DATA packets that have no chance to be delivered in time + PacketsSendDrop uint64 `json:"packetsSendDrop"` + // The total number of dropped by the SRT receiver and, as a result, + // not delivered to the upstream application DATA packets + PacketsReceivedDrop uint64 `json:"packetsReceivedDrop"` + // The total number of packets that failed to be decrypted at the receiver side + PacketsReceivedUndecrypt uint64 `json:"packetsReceivedUndecrypt"` + + // Same as packetsReceived, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) + BytesReceived uint64 `json:"bytesReceived"` + // Same as packetsSent, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) + BytesSent uint64 `json:"bytesSent"` + // Same as packetsSentUnique, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) + BytesSentUnique uint64 `json:"bytesSentUnique"` + // Same as packetsReceivedUnique, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) + BytesReceivedUnique uint64 `json:"bytesReceivedUnique"` + // Same as packetsReceivedLoss, but expressed in bytes, including payload and all the headers (IP, TCP, SRT), + // bytes for the presently missing (either reordered or lost) packets' payloads are estimated + // based on the average packet size + BytesReceivedLoss uint64 `json:"bytesReceivedLoss"` + // Same as packetsRetrans, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) + BytesRetrans uint64 `json:"bytesRetrans"` + // Same as packetsReceivedRetrans, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) + BytesReceivedRetrans uint64 `json:"bytesReceivedRetrans"` + // Same as PacketsReceivedBelated, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) + BytesReceivedBelated uint64 `json:"bytesReceivedBelated"` + // Same as packetsSendDrop, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) + BytesSendDrop uint64 `json:"bytesSendDrop"` + // Same as packetsReceivedDrop, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) + BytesReceivedDrop uint64 `json:"bytesReceivedDrop"` + // Same as packetsReceivedUndecrypt, but expressed in bytes, including payload and all the headers (IP, TCP, SRT) + BytesReceivedUndecrypt uint64 `json:"bytesReceivedUndecrypt"` + + // Current minimum time interval between which consecutive packets are sent, in microseconds + UsPacketsSendPeriod float64 `json:"usPacketsSendPeriod"` + // The maximum number of packets that can be "in flight" + PacketsFlowWindow uint64 `json:"packetsFlowWindow"` + // The number of packets in flight + PacketsFlightSize uint64 `json:"packetsFlightSize"` + // Smoothed round-trip time (SRTT), an exponentially-weighted moving average (EWMA) + // of an endpoint's RTT samples, in milliseconds + MsRTT float64 `json:"msRTT"` + // Current transmission bandwidth, in Mbps + MbpsSendRate float64 `json:"mbpsSendRate"` + // Current receiving bandwidth, in Mbps + MbpsReceiveRate float64 `json:"mbpsReceiveRate"` + // Estimated capacity of the network link, in Mbps + MbpsLinkCapacity float64 `json:"mbpsLinkCapacity"` + // The available space in the sender's buffer, in bytes + BytesAvailSendBuf uint64 `json:"bytesAvailSendBuf"` + // The available space in the receiver's buffer, in bytes + BytesAvailReceiveBuf uint64 `json:"bytesAvailReceiveBuf"` + // Transmission bandwidth limit, in Mbps + MbpsMaxBW float64 `json:"mbpsMaxBW"` + // Maximum Segment Size (MSS), in bytes + ByteMSS uint64 `json:"byteMSS"` + // The number of packets in the sender's buffer that are already scheduled + // for sending or even possibly sent, but not yet acknowledged + PacketsSendBuf uint64 `json:"packetsSendBuf"` + // Instantaneous (current) value of packetsSndBuf, but expressed in bytes, + // including payload and all headers (IP, TCP, SRT) + BytesSendBuf uint64 `json:"bytesSendBuf"` + // The timespan (msec) of packets in the sender's buffer (unacknowledged packets) + MsSendBuf uint64 `json:"msSendBuf"` + // Timestamp-based Packet Delivery Delay value of the peer + MsSendTsbPdDelay uint64 `json:"msSendTsbPdDelay"` + // The number of acknowledged packets in receiver's buffer + PacketsReceiveBuf uint64 `json:"packetsReceiveBuf"` + // Instantaneous (current) value of packetsRcvBuf, expressed in bytes, including payload and all headers (IP, TCP, SRT) + BytesReceiveBuf uint64 `json:"bytesReceiveBuf"` + // The timespan (msec) of acknowledged packets in the receiver's buffer + MsReceiveBuf uint64 `json:"msReceiveBuf"` + // Timestamp-based Packet Delivery Delay value set on the socket via SRTO_RCVLATENCY or SRTO_LATENCY + MsReceiveTsbPdDelay uint64 `json:"msReceiveTsbPdDelay"` + // Instant value of the packet reorder tolerance + PacketsReorderTolerance uint64 `json:"packetsReorderTolerance"` + // Accumulated difference between the current time and the time-to-play of a packet that is received late + PacketsReceivedAvgBelatedTime uint64 `json:"packetsReceivedAvgBelatedTime"` + // Percentage of resent data vs. sent data + PacketsSendLossRate float64 `json:"packetsSendLossRate"` + // Percentage of retransmitted data vs. received data + PacketsReceivedLossRate float64 `json:"packetsReceivedLossRate"` +} + +// APISRTConnList is a list of SRT connections. +type APISRTConnList struct { + ItemCount int `json:"itemCount"` + PageCount int `json:"pageCount"` + Items []APISRTConn `json:"items"` +} diff --git a/internal/defs/api_webrtc.go b/internal/defs/api_webrtc.go new file mode 100644 index 00000000..746f8ffa --- /dev/null +++ b/internal/defs/api_webrtc.go @@ -0,0 +1,52 @@ +package defs + +import ( + "time" + + "github.com/google/uuid" +) + +// APIWebRTCServer contains methods used by the API and Metrics server. +type APIWebRTCServer interface { + APISessionsList() (*APIWebRTCSessionList, error) + APISessionsGet(uuid.UUID) (*APIWebRTCSession, error) + APISessionsKick(uuid.UUID) error +} + +// APIWebRTCSessionState is the state of a WebRTC connection. +type APIWebRTCSessionState string + +// states. +const ( + APIWebRTCSessionStateRead APIWebRTCSessionState = "read" + APIWebRTCSessionStatePublish APIWebRTCSessionState = "publish" +) + +// APIWebRTCSession is a WebRTC session. +type APIWebRTCSession struct { + ID uuid.UUID `json:"id"` + Created time.Time `json:"created"` + RemoteAddr string `json:"remoteAddr"` + PeerConnectionEstablished bool `json:"peerConnectionEstablished"` + LocalCandidate string `json:"localCandidate"` + RemoteCandidate string `json:"remoteCandidate"` + State APIWebRTCSessionState `json:"state"` + Path string `json:"path"` + Query string `json:"query"` + User string `json:"user"` + BytesReceived uint64 `json:"bytesReceived"` + BytesSent uint64 `json:"bytesSent"` + RTPPacketsReceived uint64 `json:"rtpPacketsReceived"` + RTPPacketsSent uint64 `json:"rtpPacketsSent"` + RTPPacketsLost uint64 `json:"rtpPacketsLost"` + RTPPacketsJitter float64 `json:"rtpPacketsJitter"` + RTCPPacketsReceived uint64 `json:"rtcpPacketsReceived"` + RTCPPacketsSent uint64 `json:"rtcpPacketsSent"` +} + +// APIWebRTCSessionList is a list of WebRTC sessions. +type APIWebRTCSessionList struct { + ItemCount int `json:"itemCount"` + PageCount int `json:"pageCount"` + Items []APIWebRTCSession `json:"items"` +} diff --git a/internal/defs/source.go b/internal/defs/source.go index 997ee48d..673f2830 100644 --- a/internal/defs/source.go +++ b/internal/defs/source.go @@ -20,17 +20,14 @@ type Source interface { APISourceDescribe() *APIPathSource } -// FormatsToCodecs returns the name of codecs of given formats. -func FormatsToCodecs(formats []format.Format) []string { - ret := make([]string, len(formats)) - for i, forma := range formats { - ret[i] = forma.Codec() - } - return ret -} - // FormatsInfo returns a description of formats. func FormatsInfo(formats []format.Format) string { + codecs := FormatsToCodecs(formats) + codecNames := make([]string, len(codecs)) + for i, codec := range codecs { + codecNames[i] = string(codec) + } + return fmt.Sprintf("%d %s (%s)", len(formats), func() string { @@ -39,32 +36,7 @@ func FormatsInfo(formats []format.Format) string { } return "tracks" }(), - strings.Join(FormatsToCodecs(formats), ", ")) -} - -func gatherFormats(medias []*description.Media) []format.Format { - n := 0 - for _, media := range medias { - n += len(media.Formats) - } - - if n == 0 { - return nil - } - - formats := make([]format.Format, n) - n = 0 - - for _, media := range medias { - n += copy(formats[n:], media.Formats) - } - - return formats -} - -// MediasToCodecs returns the name of codecs of given formats. -func MediasToCodecs(medias []*description.Media) []string { - return FormatsToCodecs(gatherFormats(medias)) + strings.Join(codecNames, ", ")) } // MediasInfo returns a description of medias. diff --git a/internal/hooks/on_connect.go b/internal/hooks/on_connect.go index 1fd99486..77bed424 100644 --- a/internal/hooks/on_connect.go +++ b/internal/hooks/on_connect.go @@ -28,7 +28,7 @@ func OnConnect(params OnConnectParams) func() { _, port, _ := net.SplitHostPort(params.RTSPAddress) env = externalcmd.Environment{ "RTSP_PORT": port, - "MTX_CONN_TYPE": params.Desc.Type, + "MTX_CONN_TYPE": string(params.Desc.Type), "MTX_CONN_ID": params.Desc.ID, } } diff --git a/internal/hooks/on_read.go b/internal/hooks/on_read.go index 7a5cf261..f78ff50b 100644 --- a/internal/hooks/on_read.go +++ b/internal/hooks/on_read.go @@ -26,7 +26,7 @@ func OnRead(params OnReadParams) func() { env = params.ExternalCmdEnv desc := params.Reader env["MTX_QUERY"] = params.Query - env["MTX_READER_TYPE"] = desc.Type + env["MTX_READER_TYPE"] = string(desc.Type) env["MTX_READER_ID"] = desc.ID } diff --git a/internal/hooks/on_ready.go b/internal/hooks/on_ready.go index 67327232..0bae41ba 100644 --- a/internal/hooks/on_ready.go +++ b/internal/hooks/on_ready.go @@ -26,7 +26,7 @@ func OnReady(params OnReadyParams) func() { env = params.ExternalCmdEnv env["MTX_QUERY"] = params.Query if params.Desc != nil { - env["MTX_SOURCE_TYPE"] = params.Desc.Type + env["MTX_SOURCE_TYPE"] = string(params.Desc.Type) env["MTX_SOURCE_ID"] = params.Desc.ID } } diff --git a/internal/linters/go2api/go2api_test.go b/internal/linters/go2api/go2api_test.go index 0320d413..76348abc 100644 --- a/internal/linters/go2api/go2api_test.go +++ b/internal/linters/go2api/go2api_test.go @@ -17,16 +17,35 @@ import ( ) type openAPIProperty struct { - Ref string `yaml:"$ref"` - Type string `yaml:"type"` - Format string `yaml:"format"` - Nullable bool `yaml:"nullable"` - Deprecated bool `yaml:"deprecated"` - Items *openAPIProperty `yaml:"items"` + Ref string `yaml:"$ref"` + Type string `yaml:"type"` + Format string `yaml:"format"` + AllOf []openAPIProperty `yaml:"allOf"` + Nullable bool `yaml:"nullable"` + Deprecated bool `yaml:"deprecated"` + Enum []string `yaml:"enum"` + Items *openAPIProperty `yaml:"items"` +} + +func wrapRef(rt reflect.Type, p openAPIProperty) openAPIProperty { + if p.Ref == "" { + return p + } + + if _, ok := goEnumToApi(rt); ok { + p.Type = "string" + } else if rt.Kind() == reflect.Struct { + p.Type = "object" + } + + p.AllOf = []openAPIProperty{{Ref: p.Ref}} + p.Ref = "" + return p } type openAPISchema struct { Type string `yaml:"type"` + Enum []string `yaml:"enum"` Properties map[string]openAPIProperty `yaml:"properties"` } @@ -36,13 +55,28 @@ type openAPI struct { } `yaml:"components"` } -func fillProperty(t *testing.T, rt reflect.Type) openAPIProperty { +func schemaName(rt reflect.Type) string { + name := strings.TrimPrefix(rt.Name(), "API") + + if rt.PkgPath() == "github.com/bluenviron/mediamtx/internal/conf" && name == "Path" { + return "PathConf" + } + + return name +} + +func goStructToApi(t *testing.T, rt reflect.Type) openAPIProperty { if rt.Kind() == reflect.Pointer { - prop := fillProperty(t, rt.Elem()) + prop := goStructToApi(t, rt.Elem()) + prop = wrapRef(rt.Elem(), prop) prop.Nullable = true return prop } + if _, ok := goEnumToApi(rt); ok { + return openAPIProperty{Ref: "#/components/schemas/" + schemaName(rt)} + } + switch { case rt == reflect.TypeOf(""): return openAPIProperty{Type: "string"} @@ -69,22 +103,7 @@ func fillProperty(t *testing.T, rt reflect.Type) openAPIProperty { rt == reflect.TypeOf(conf.Duration(0)) || rt == reflect.TypeOf(conf.IPNetwork{}) || rt == reflect.TypeOf(conf.Credential("")) || - rt == reflect.TypeOf(conf.RecordFormat("")) || - rt == reflect.TypeOf(conf.AuthAction("")) || - rt == reflect.TypeOf(conf.Encryption("")) || - rt == reflect.TypeOf(conf.RTSPTransport{}) || - rt == reflect.TypeOf(conf.StringSize(0)) || - rt == reflect.TypeOf(conf.RTSPRangeType("")) || - rt == reflect.TypeOf(conf.LogLevel(0)) || - rt == reflect.TypeOf(conf.AuthMethod("")) || - rt == reflect.TypeOf(conf.LogDestination(0)) || - rt == reflect.TypeOf(conf.RTSPAuthMethod(0)) || - rt == reflect.TypeOf(conf.HLSVariant(0)) || - rt == reflect.TypeOf(defs.APIRTMPConnState("")) || - rt == reflect.TypeOf(defs.APIWebRTCSessionState("")) || - rt == reflect.TypeOf(defs.APISRTConnState("")) || - rt == reflect.TypeOf(defs.APIRTSPSessionState("")) || - rt == reflect.TypeOf(conf.Codec("")): + rt == reflect.TypeOf(conf.StringSize(0)): return openAPIProperty{Type: "string"} case rt == reflect.TypeOf(conf.RTSPTransports{}): @@ -92,21 +111,17 @@ func fillProperty(t *testing.T, rt reflect.Type) openAPIProperty { Type: "array", Items: &openAPIProperty{ Type: "string", + Enum: []string{"udp", "multicast", "tcp"}, }, } case rt.Kind() == reflect.Struct: - schemaName := strings.TrimPrefix(rt.Name(), "API") - if rt.PkgPath() == "github.com/bluenviron/mediamtx/internal/conf" && schemaName == "Path" { - schemaName = "PathConf" - } - return openAPIProperty{ - Ref: "#/components/schemas/" + schemaName, + Ref: "#/components/schemas/" + schemaName(rt), } case rt.Kind() == reflect.Slice: - items := fillProperty(t, rt.Elem()) + items := goStructToApi(t, rt.Elem()) return openAPIProperty{ Type: "array", Items: &items, @@ -118,6 +133,175 @@ func fillProperty(t *testing.T, rt reflect.Type) openAPIProperty { } } +func goEnumToApi(rt reflect.Type) (openAPISchema, bool) { + switch rt { + case reflect.TypeOf(defs.APIOKStatus("")): + return openAPISchema{Type: "string", Enum: []string{"ok"}}, true + + case reflect.TypeOf(defs.APIErrorStatus("")): + return openAPISchema{Type: "string", Enum: []string{"error"}}, true + + case reflect.TypeOf(defs.APIPathSourceType("")): + return openAPISchema{Type: "string", Enum: []string{ + "hlsSource", + "redirect", + "rpiCameraSource", + "rtmpConn", + "rtmpsConn", + "rtmpSource", + "rtspSession", + "rtspSource", + "rtspsSession", + "srtConn", + "srtSource", + "mpegtsSource", + "rtpSource", + "webRTCSession", + "webRTCSource", + }}, true + + case reflect.TypeOf(defs.APIPathReaderType("")): + return openAPISchema{Type: "string", Enum: []string{ + "hlsMuxer", + "rpiCameraSecondary", + "rtmpConn", + "rtmpsConn", + "rtspConn", + "rtspSession", + "rtspsConn", + "rtspsSession", + "srtConn", + "webRTCSession", + }}, true + + case reflect.TypeOf(defs.APIPathTrackCodec("")): + return openAPISchema{Type: "string", Enum: []string{ + "AV1", + "VP9", + "VP8", + "H265", + "H264", + "MPEG-4 Video", + "MPEG-1 Video", + "MJPEG", + "Opus", + "Vorbis", + "MPEG-4 Audio", + "MPEG-4 Audio LATM", + "MPEG-1 Audio", + "AC3", + "Speex", + "G726", + "G722", + "G711", + "LPCM", + "MPEG-TS", + "KLV", + "Generic", + }}, true + + case reflect.TypeOf(conf.AlwaysAvailableTrackCodec("")): + return openAPISchema{Type: "string", Enum: []string{ + "AV1", + "VP9", + "H265", + "H264", + "MPEG4Audio", + "Opus", + "G711", + "LPCM", + }}, true + + case reflect.TypeOf(conf.AuthAction("")): + return openAPISchema{Type: "string", Enum: []string{ + "publish", + "read", + "playback", + "api", + "metrics", + "pprof", + }}, true + + case reflect.TypeOf(conf.AuthMethod("")): + return openAPISchema{Type: "string", Enum: []string{ + "internal", + "http", + "jwt", + }}, true + + case reflect.TypeOf(conf.Encryption("")): + return openAPISchema{Type: "string", Enum: []string{ + "no", + "optional", + "strict", + }}, true + + case reflect.TypeOf(conf.HLSVariant(0)): + return openAPISchema{Type: "string", Enum: []string{ + "mpegts", + "fmp4", + "lowLatency", + }}, true + + case reflect.TypeOf(conf.LogDestination(0)): + return openAPISchema{Type: "string", Enum: []string{ + "stdout", + "file", + "syslog", + }}, true + + case reflect.TypeOf(conf.LogLevel(0)): + return openAPISchema{Type: "string", Enum: []string{ + "error", + "warn", + "info", + "debug", + }}, true + + case reflect.TypeOf(conf.RecordFormat("")): + return openAPISchema{Type: "string", Enum: []string{ + "fmp4", + "mpegts", + }}, true + + case reflect.TypeOf(conf.RTSPAuthMethod(0)): + return openAPISchema{Type: "string", Enum: []string{ + "basic", + "digest", + }}, true + + case reflect.TypeOf(conf.RTSPRangeType("")): + return openAPISchema{Type: "string", Enum: []string{ + "", + "clock", + "npt", + "smpte", + }}, true + + case reflect.TypeOf(conf.RTSPTransport{}): + return openAPISchema{Type: "string", Enum: []string{ + "udp", + "multicast", + "tcp", + "automatic", + }}, true + + case reflect.TypeOf(defs.APIRTMPConnState("")): + return openAPISchema{Type: "string", Enum: []string{"idle", "read", "publish"}}, true + + case reflect.TypeOf(defs.APIWebRTCSessionState("")): + return openAPISchema{Type: "string", Enum: []string{"read", "publish"}}, true + + case reflect.TypeOf(defs.APISRTConnState("")): + return openAPISchema{Type: "string", Enum: []string{"idle", "read", "publish"}}, true + + case reflect.TypeOf(defs.APIRTSPSessionState("")): + return openAPISchema{Type: "string", Enum: []string{"idle", "read", "publish"}}, true + } + + return openAPISchema{}, false +} + func TestGo2API(t *testing.T) { byts, err := os.ReadFile("../../../api/openapi.yaml") require.NoError(t, err) @@ -126,140 +310,177 @@ func TestGo2API(t *testing.T) { err = yaml.Unmarshal(byts, &doc) require.NoError(t, err) - for _, ca := range []struct { - openAPIKey string - goStruct any - }{ - { - "AlwaysAvailableTrack", - conf.AlwaysAvailableTrack{}, - }, - { - "AuthInternalUser", - conf.AuthInternalUser{}, - }, - { - "AuthInternalUserPermission", - conf.AuthInternalUserPermission{}, - }, - { - "GlobalConf", - conf.Conf{}, - }, - { - "HLSMuxer", - defs.APIHLSMuxer{}, - }, - { - "HLSMuxerList", - defs.APIHLSMuxerList{}, - }, - { - "Info", - defs.APIInfo{}, - }, - { - "Path", - defs.APIPath{}, - }, - { - "PathConf", - conf.Path{}, - }, - { - "PathConfList", - defs.APIPathConfList{}, - }, - { - "PathList", - defs.APIPathList{}, - }, - { - "PathReader", - defs.APIPathReader{}, - }, - { - "PathSource", - defs.APIPathSource{}, - }, - { - "Recording", - defs.APIRecording{}, - }, - { - "RecordingList", - defs.APIRecordingList{}, - }, - { - "RecordingSegment", - defs.APIRecordingSegment{}, - }, - { - "RTMPConn", - defs.APIRTMPConn{}, - }, - { - "RTMPConnList", - defs.APIRTMPConnList{}, - }, - { - "RTSPConn", - defs.APIRTSPConn{}, - }, - { - "RTSPConnList", - defs.APIRTSPConnsList{}, - }, - { - "RTSPSession", - defs.APIRTSPSession{}, - }, - { - "RTSPSessionList", - defs.APIRTSPSessionList{}, - }, - { - "SRTConn", - defs.APISRTConn{}, - }, - { - "SRTConnList", - defs.APISRTConnList{}, - }, - { - "WebRTCSession", - defs.APIWebRTCSession{}, - }, - { - "WebRTCSessionList", - defs.APIWebRTCSessionList{}, - }, - } { - t.Run(ca.openAPIKey, func(t *testing.T) { - content1 := doc.Components.Schemas[ca.openAPIKey] + t.Run("structs", func(t *testing.T) { + for _, ca := range []struct { + openAPIKey string + goStruct any + }{ + { + "AlwaysAvailableTrack", + conf.AlwaysAvailableTrack{}, + }, + { + "AuthInternalUser", + conf.AuthInternalUser{}, + }, + { + "AuthInternalUserPermission", + conf.AuthInternalUserPermission{}, + }, + { + "GlobalConf", + conf.Conf{}, + }, + { + "HLSMuxer", + defs.APIHLSMuxer{}, + }, + { + "HLSMuxerList", + defs.APIHLSMuxerList{}, + }, + { + "Info", + defs.APIInfo{}, + }, + { + "Path", + defs.APIPath{}, + }, + { + "PathConf", + conf.Path{}, + }, + { + "PathConfList", + defs.APIPathConfList{}, + }, + { + "PathList", + defs.APIPathList{}, + }, + { + "PathReader", + defs.APIPathReader{}, + }, + { + "PathSource", + defs.APIPathSource{}, + }, + { + "Recording", + defs.APIRecording{}, + }, + { + "RecordingList", + defs.APIRecordingList{}, + }, + { + "RecordingSegment", + defs.APIRecordingSegment{}, + }, + { + "RTMPConn", + defs.APIRTMPConn{}, + }, + { + "RTMPConnList", + defs.APIRTMPConnList{}, + }, + { + "RTSPConn", + defs.APIRTSPConn{}, + }, + { + "RTSPConnList", + defs.APIRTSPConnsList{}, + }, + { + "RTSPSession", + defs.APIRTSPSession{}, + }, + { + "RTSPSessionList", + defs.APIRTSPSessionList{}, + }, + { + "SRTConn", + defs.APISRTConn{}, + }, + { + "SRTConnList", + defs.APISRTConnList{}, + }, + { + "WebRTCSession", + defs.APIWebRTCSession{}, + }, + { + "WebRTCSessionList", + defs.APIWebRTCSessionList{}, + }, + } { + t.Run(ca.openAPIKey, func(t *testing.T) { + content1 := doc.Components.Schemas[ca.openAPIKey] - content2 := openAPISchema{ - Type: "object", - Properties: make(map[string]openAPIProperty), - } - - ty := reflect.TypeOf(ca.goStruct) - - for i := range ty.NumField() { - sf := ty.Field(i) - js := sf.Tag.Get("json") - name, _, _ := strings.Cut(js, ",") - deprecated := sf.Tag.Get("deprecated") == "true" - - if name != "" && name != "-" && name != "paths" && name != "pathDefaults" && - (!strings.Contains(js, ",omitempty") || deprecated) { - prop := fillProperty(t, sf.Type) - prop.Deprecated = deprecated - content2.Properties[name] = prop + content2 := openAPISchema{ + Type: "object", + Properties: make(map[string]openAPIProperty), } - } - require.Equal(t, content2, content1) - }) - } + ty := reflect.TypeOf(ca.goStruct) + + for i := range ty.NumField() { + sf := ty.Field(i) + js := sf.Tag.Get("json") + name, _, _ := strings.Cut(js, ",") + deprecated := sf.Tag.Get("deprecated") == "true" + + if name != "" && name != "-" && name != "paths" && name != "pathDefaults" && + (!strings.Contains(js, ",omitempty") || deprecated) { + prop := goStructToApi(t, sf.Type) + prop.Deprecated = deprecated + if deprecated { + prop = wrapRef(sf.Type, prop) + } + content2.Properties[name] = prop + } + } + + require.Equal(t, content2, content1) + }) + } + }) + + t.Run("enums", func(t *testing.T) { + for _, rt := range []reflect.Type{ + reflect.TypeOf(defs.APIOKStatus("")), + reflect.TypeOf(defs.APIErrorStatus("")), + reflect.TypeOf(defs.APIPathSourceType("")), + reflect.TypeOf(defs.APIPathReaderType("")), + reflect.TypeOf(defs.APIPathTrackCodec("")), + reflect.TypeOf(conf.AlwaysAvailableTrackCodec("")), + reflect.TypeOf(conf.AuthAction("")), + reflect.TypeOf(conf.AuthMethod("")), + reflect.TypeOf(conf.Encryption("")), + reflect.TypeOf(conf.HLSVariant(0)), + reflect.TypeOf(conf.LogDestination(0)), + reflect.TypeOf(conf.LogLevel(0)), + reflect.TypeOf(conf.RecordFormat("")), + reflect.TypeOf(conf.RTSPAuthMethod(0)), + reflect.TypeOf(conf.RTSPRangeType("")), + reflect.TypeOf(conf.RTSPTransport{}), + reflect.TypeOf(defs.APIRTMPConnState("")), + reflect.TypeOf(defs.APIRTSPSessionState("")), + reflect.TypeOf(defs.APISRTConnState("")), + reflect.TypeOf(defs.APIWebRTCSessionState("")), + } { + t.Run(rt.Name(), func(t *testing.T) { + content1 := doc.Components.Schemas[schemaName(rt)] + content2, ok := goEnumToApi(rt) + require.True(t, ok) + require.Equal(t, content2, content1) + }) + } + }) } diff --git a/internal/metrics/metrics.go b/internal/metrics/metrics.go index 77d9929e..94526f44 100644 --- a/internal/metrics/metrics.go +++ b/internal/metrics/metrics.go @@ -161,7 +161,7 @@ func (m *Metrics) middlewareAuth(ctx *gin.Context) { if err.AskCredentials { ctx.Header("WWW-Authenticate", `Basic realm="mediamtx"`) ctx.AbortWithStatusJSON(http.StatusUnauthorized, &defs.APIError{ - Status: "error", + Status: defs.APIErrorStatusError, Error: "authentication error", }) return @@ -173,7 +173,7 @@ func (m *Metrics) middlewareAuth(ctx *gin.Context) { <-time.After(auth.PauseAfterError) ctx.AbortWithStatusJSON(http.StatusUnauthorized, &defs.APIError{ - Status: "error", + Status: defs.APIErrorStatusError, Error: "authentication error", }) return diff --git a/internal/metrics/metrics_test.go b/internal/metrics/metrics_test.go index e1560c48..481ae7fe 100644 --- a/internal/metrics/metrics_test.go +++ b/internal/metrics/metrics_test.go @@ -32,17 +32,17 @@ func (dummyPathManager) APIPathsList() (*defs.APIPathList, error) { Name: "mypath", ConfName: "mypathconf", Source: &defs.APIPathSource{ - Type: "testing", + Type: defs.APIPathSourceTypeRTSPSession, ID: "123324354", }, Ready: true, ReadyTime: ptrOf(time.Date(2003, 11, 4, 23, 15, 7, 0, time.UTC)), - Tracks: []string{"H264", "H265"}, + Tracks: []defs.APIPathTrackCodec{defs.APIPathTrackCodecH264, defs.APIPathTrackCodecH265}, BytesReceived: 123, BytesSent: 456, Readers: []defs.APIPathReader{ { - Type: "testing", + Type: defs.APIPathReaderTypeRTSPSession, ID: "345234423", }, }, diff --git a/internal/playback/server.go b/internal/playback/server.go index 3c606a45..290e0ee6 100644 --- a/internal/playback/server.go +++ b/internal/playback/server.go @@ -129,7 +129,7 @@ func (s *Server) doAuth(ctx *gin.Context, pathName string) bool { if err.AskCredentials { ctx.Header("WWW-Authenticate", `Basic realm="mediamtx"`) ctx.AbortWithStatusJSON(http.StatusUnauthorized, &defs.APIError{ - Status: "error", + Status: defs.APIErrorStatusError, Error: "authentication error", }) return false @@ -142,7 +142,7 @@ func (s *Server) doAuth(ctx *gin.Context, pathName string) bool { <-time.After(auth.PauseAfterError) ctx.AbortWithStatusJSON(http.StatusUnauthorized, &defs.APIError{ - Status: "error", + Status: defs.APIErrorStatusError, Error: "authentication error", }) return false diff --git a/internal/pprof/pprof.go b/internal/pprof/pprof.go index ef5354b8..fa29fcd3 100644 --- a/internal/pprof/pprof.go +++ b/internal/pprof/pprof.go @@ -108,7 +108,7 @@ func (pp *PPROF) middlewareAuth(ctx *gin.Context) { if err.AskCredentials { ctx.Header("WWW-Authenticate", `Basic realm="mediamtx"`) ctx.AbortWithStatusJSON(http.StatusUnauthorized, &defs.APIError{ - Status: "error", + Status: defs.APIErrorStatusError, Error: "authentication error", }) return @@ -120,7 +120,7 @@ func (pp *PPROF) middlewareAuth(ctx *gin.Context) { <-time.After(auth.PauseAfterError) ctx.AbortWithStatusJSON(http.StatusUnauthorized, &defs.APIError{ - Status: "error", + Status: defs.APIErrorStatusError, Error: "authentication error", }) return diff --git a/internal/servers/hls/http_server.go b/internal/servers/hls/http_server.go index 2282eef2..86204976 100644 --- a/internal/servers/hls/http_server.go +++ b/internal/servers/hls/http_server.go @@ -161,7 +161,7 @@ func (s *httpServer) onRequest(ctx *gin.Context) { if terr.AskCredentials { ctx.Header("WWW-Authenticate", `Basic realm="mediamtx"`) ctx.AbortWithStatusJSON(http.StatusUnauthorized, &defs.APIError{ - Status: "error", + Status: defs.APIErrorStatusError, Error: "authentication error", }) return @@ -173,7 +173,7 @@ func (s *httpServer) onRequest(ctx *gin.Context) { <-time.After(auth.PauseAfterError) ctx.AbortWithStatusJSON(http.StatusUnauthorized, &defs.APIError{ - Status: "error", + Status: defs.APIErrorStatusError, Error: "authentication error", }) return diff --git a/internal/servers/hls/muxer.go b/internal/servers/hls/muxer.go index 98159c1e..752368ad 100644 --- a/internal/servers/hls/muxer.go +++ b/internal/servers/hls/muxer.go @@ -249,7 +249,7 @@ func (m *muxer) getInstance() *muxerInstance { // APIReaderDescribe implements reader. func (m *muxer) APIReaderDescribe() *defs.APIPathReader { return &defs.APIPathReader{ - Type: "hlsMuxer", + Type: defs.APIPathReaderTypeHLSMuxer, ID: "", } } diff --git a/internal/servers/rtmp/conn.go b/internal/servers/rtmp/conn.go index bc41a737..d6276671 100644 --- a/internal/servers/rtmp/conn.go +++ b/internal/servers/rtmp/conn.go @@ -291,11 +291,11 @@ func (c *conn) runPublish() error { // APIReaderDescribe implements reader. func (c *conn) APIReaderDescribe() *defs.APIPathReader { return &defs.APIPathReader{ - Type: func() string { + Type: func() defs.APIPathReaderType { if c.isTLS { - return "rtmpsConn" + return defs.APIPathReaderTypeRTMPSConn } - return "rtmpConn" + return defs.APIPathReaderTypeRTMPConn }(), ID: c.uuid.String(), } @@ -304,11 +304,11 @@ func (c *conn) APIReaderDescribe() *defs.APIPathReader { // APISourceDescribe implements source. func (c *conn) APISourceDescribe() *defs.APIPathSource { return &defs.APIPathSource{ - Type: func() string { + Type: func() defs.APIPathSourceType { if c.isTLS { - return "rtmpsConn" + return defs.APIPathSourceTypeRTMPSConn } - return "rtmpConn" + return defs.APIPathSourceTypeRTMPConn }(), ID: c.uuid.String(), } diff --git a/internal/servers/rtsp/conn.go b/internal/servers/rtsp/conn.go index 29819c30..67b62a4a 100644 --- a/internal/servers/rtsp/conn.go +++ b/internal/servers/rtsp/conn.go @@ -86,11 +86,11 @@ func (c *conn) initialize() { RunOnDisconnect: c.runOnDisconnect, RTSPAddress: c.rtspAddress, Desc: defs.APIPathReader{ - Type: func() string { + Type: func() defs.APIPathReaderType { if c.isTLS { - return "rtspsConn" + return defs.APIPathReaderTypeRTSPSConn } - return "rtspConn" + return defs.APIPathReaderTypeRTSPConn }(), ID: c.uuid.String(), }, diff --git a/internal/servers/rtsp/session.go b/internal/servers/rtsp/session.go index 57947e8e..1bea0229 100644 --- a/internal/servers/rtsp/session.go +++ b/internal/servers/rtsp/session.go @@ -380,11 +380,11 @@ func (s *session) onPause(_ *gortsplib.ServerHandlerOnPauseCtx) (*base.Response, // APIReaderDescribe implements reader. func (s *session) APIReaderDescribe() *defs.APIPathReader { return &defs.APIPathReader{ - Type: func() string { + Type: func() defs.APIPathReaderType { if s.isTLS { - return "rtspsSession" + return defs.APIPathReaderTypeRTSPSSession } - return "rtspSession" + return defs.APIPathReaderTypeRTSPSession }(), ID: s.uuid.String(), } @@ -393,11 +393,11 @@ func (s *session) APIReaderDescribe() *defs.APIPathReader { // APISourceDescribe implements source. func (s *session) APISourceDescribe() *defs.APIPathSource { return &defs.APIPathSource{ - Type: func() string { + Type: func() defs.APIPathSourceType { if s.isTLS { - return "rtspsSession" + return defs.APIPathSourceTypeRTSPSSession } - return "rtspSession" + return defs.APIPathSourceTypeRTSPSession }(), ID: s.uuid.String(), } diff --git a/internal/servers/srt/conn.go b/internal/servers/srt/conn.go index 1e5b3661..a3658348 100644 --- a/internal/servers/srt/conn.go +++ b/internal/servers/srt/conn.go @@ -347,7 +347,7 @@ func (c *conn) runRead(streamID *streamID) error { // APIReaderDescribe implements reader. func (c *conn) APIReaderDescribe() *defs.APIPathReader { return &defs.APIPathReader{ - Type: "srtConn", + Type: defs.APIPathReaderTypeSRTConn, ID: c.uuid.String(), } } @@ -355,7 +355,7 @@ func (c *conn) APIReaderDescribe() *defs.APIPathReader { // APISourceDescribe implements source. func (c *conn) APISourceDescribe() *defs.APIPathSource { return &defs.APIPathSource{ - Type: "srtConn", + Type: defs.APIPathSourceTypeSRTConn, ID: c.uuid.String(), } } diff --git a/internal/servers/webrtc/http_server.go b/internal/servers/webrtc/http_server.go index a2be1512..0adc4692 100644 --- a/internal/servers/webrtc/http_server.go +++ b/internal/servers/webrtc/http_server.go @@ -49,7 +49,7 @@ func mergePathAndQuery(path string, rawQuery string) string { func writeError(ctx *gin.Context, statusCode int, err error) { ctx.JSON(statusCode, &defs.APIError{ - Status: "error", + Status: defs.APIErrorStatusError, Error: err.Error(), }) } @@ -143,7 +143,7 @@ func (s *httpServer) checkAuthOutsideSession(ctx *gin.Context, pathName string, if terr.AskCredentials { ctx.Header("WWW-Authenticate", `Basic realm="mediamtx"`) ctx.AbortWithStatusJSON(http.StatusUnauthorized, &defs.APIError{ - Status: "error", + Status: defs.APIErrorStatusError, Error: "authentication error", }) return false @@ -208,7 +208,7 @@ func (s *httpServer) onWHIPPost(ctx *gin.Context, pathName string, publish bool) if terr.AskCredentials { ctx.Header("WWW-Authenticate", `Basic realm="mediamtx"`) ctx.AbortWithStatusJSON(http.StatusUnauthorized, &defs.APIError{ - Status: "error", + Status: defs.APIErrorStatusError, Error: "authentication error", }) return @@ -220,7 +220,7 @@ func (s *httpServer) onWHIPPost(ctx *gin.Context, pathName string, publish bool) <-time.After(auth.PauseAfterError) ctx.AbortWithStatusJSON(http.StatusUnauthorized, &defs.APIError{ - Status: "error", + Status: defs.APIErrorStatusError, Error: "authentication error", }) return @@ -288,7 +288,7 @@ func (s *httpServer) onWHIPPatch(ctx *gin.Context, pathName string, rawSecret st } ctx.AbortWithStatusJSON(http.StatusNoContent, &defs.APIOK{ - Status: "ok", + Status: defs.APIOKStatusOK, }) } @@ -313,7 +313,7 @@ func (s *httpServer) onWHIPDelete(ctx *gin.Context, pathName string, rawSecret s } ctx.AbortWithStatusJSON(http.StatusOK, &defs.APIOK{ - Status: "ok", + Status: defs.APIOKStatusOK, }) } diff --git a/internal/servers/webrtc/session.go b/internal/servers/webrtc/session.go index 2743043a..37315397 100644 --- a/internal/servers/webrtc/session.go +++ b/internal/servers/webrtc/session.go @@ -451,7 +451,7 @@ func (s *session) addCandidates( // APIReaderDescribe implements reader. func (s *session) APIReaderDescribe() *defs.APIPathReader { return &defs.APIPathReader{ - Type: "webRTCSession", + Type: defs.APIPathReaderTypeWebRTCSession, ID: s.uuid.String(), } } @@ -459,7 +459,7 @@ func (s *session) APIReaderDescribe() *defs.APIPathReader { // APISourceDescribe implements source. func (s *session) APISourceDescribe() *defs.APIPathSource { return &defs.APIPathSource{ - Type: "webRTCSession", + Type: defs.APIPathSourceTypeWebRTCSession, ID: s.uuid.String(), } } diff --git a/internal/staticsources/hls/source.go b/internal/staticsources/hls/source.go index b7617fbc..01439ef0 100644 --- a/internal/staticsources/hls/source.go +++ b/internal/staticsources/hls/source.go @@ -157,7 +157,7 @@ func (s *Source) Run(params defs.StaticSourceRunParams) error { // APISourceDescribe implements StaticSource. func (*Source) APISourceDescribe() *defs.APIPathSource { return &defs.APIPathSource{ - Type: "hlsSource", + Type: defs.APIPathSourceTypeHLSSource, ID: "", } } diff --git a/internal/staticsources/mpegts/source.go b/internal/staticsources/mpegts/source.go index a17b4312..62f62e40 100644 --- a/internal/staticsources/mpegts/source.go +++ b/internal/staticsources/mpegts/source.go @@ -183,7 +183,7 @@ func (s *Source) runReader(nc net.Conn) error { // APISourceDescribe implements StaticSource. func (*Source) APISourceDescribe() *defs.APIPathSource { return &defs.APIPathSource{ - Type: "mpegtsSource", + Type: defs.APIPathSourceTypeMPEGTSSource, ID: "", } } diff --git a/internal/staticsources/rpicamera/source.go b/internal/staticsources/rpicamera/source.go index 70bb2468..20e0d80a 100644 --- a/internal/staticsources/rpicamera/source.go +++ b/internal/staticsources/rpicamera/source.go @@ -95,7 +95,7 @@ func (r *secondaryReader) Close() { // APIReaderDescribe implements reader. func (*secondaryReader) APIReaderDescribe() *defs.APIPathReader { return &defs.APIPathReader{ - Type: "rpiCameraSecondary", + Type: defs.APIPathReaderTypeRPICameraSecondary, ID: "", } } @@ -361,7 +361,7 @@ func (s *Source) waitForPrimary( // APISourceDescribe implements StaticSource. func (*Source) APISourceDescribe() *defs.APIPathSource { return &defs.APIPathSource{ - Type: "rpiCameraSource", + Type: defs.APIPathSourceTypeRPICameraSource, ID: "", } } diff --git a/internal/staticsources/rtmp/source.go b/internal/staticsources/rtmp/source.go index 082fc5e2..6c44bfdd 100644 --- a/internal/staticsources/rtmp/source.go +++ b/internal/staticsources/rtmp/source.go @@ -152,7 +152,7 @@ func (s *Source) runReader(conn *gortmplib.Client) error { // APISourceDescribe implements StaticSource. func (*Source) APISourceDescribe() *defs.APIPathSource { return &defs.APIPathSource{ - Type: "rtmpSource", + Type: defs.APIPathSourceTypeRTMPSource, ID: "", } } diff --git a/internal/staticsources/rtp/source.go b/internal/staticsources/rtp/source.go index 95ac6d3c..5b759570 100644 --- a/internal/staticsources/rtp/source.go +++ b/internal/staticsources/rtp/source.go @@ -257,7 +257,7 @@ func (s *Source) runReader(desc *description.Session, nc net.Conn) error { // APISourceDescribe implements StaticSource. func (*Source) APISourceDescribe() *defs.APIPathSource { return &defs.APIPathSource{ - Type: "rtpSource", + Type: defs.APIPathSourceTypeRTPSource, ID: "", } } diff --git a/internal/staticsources/rtsp/source.go b/internal/staticsources/rtsp/source.go index 04193ad5..e9147203 100644 --- a/internal/staticsources/rtsp/source.go +++ b/internal/staticsources/rtsp/source.go @@ -286,7 +286,7 @@ func (s *Source) runInner(c *gortsplib.Client, u *base.URL, pathConf *conf.Path) // APISourceDescribe implements StaticSource. func (*Source) APISourceDescribe() *defs.APIPathSource { return &defs.APIPathSource{ - Type: "rtspSource", + Type: defs.APIPathSourceTypeRTSPSource, ID: "", } } diff --git a/internal/staticsources/srt/source.go b/internal/staticsources/srt/source.go index 8dcd67c3..8fd46192 100644 --- a/internal/staticsources/srt/source.go +++ b/internal/staticsources/srt/source.go @@ -130,7 +130,7 @@ func (s *Source) runReader(sconn srt.Conn) error { // APISourceDescribe implements StaticSource. func (*Source) APISourceDescribe() *defs.APIPathSource { return &defs.APIPathSource{ - Type: "srtSource", + Type: defs.APIPathSourceTypeSRTSource, ID: "", } } diff --git a/internal/staticsources/webrtc/source.go b/internal/staticsources/webrtc/source.go index 2344f17c..f6f56e07 100644 --- a/internal/staticsources/webrtc/source.go +++ b/internal/staticsources/webrtc/source.go @@ -133,7 +133,7 @@ func (s *Source) Run(params defs.StaticSourceRunParams) error { // APISourceDescribe implements StaticSource. func (*Source) APISourceDescribe() *defs.APIPathSource { return &defs.APIPathSource{ - Type: "webRTCSource", + Type: defs.APIPathSourceTypeWebRTCSource, ID: "", } }