api: add new stats (#5582)

- RTSPSession.outboundRTPPacketsDiscarded
- Path.inboundFramesInError
- SRTConn.outboundFramesDiscarded
- WebRTCSession.outboundFramesDiscarded
- RTMPConn.outboundFramesDiscarded
- HLSMuxer.outboundFramesDiscarded
This commit is contained in:
Alessandro Ros
2026-03-17 17:22:15 +01:00
committed by GitHub
parent adf45596a3
commit c8e07b55d5
31 changed files with 658 additions and 417 deletions
+38
View File
@@ -973,12 +973,23 @@ components:
type: array type: array
items: items:
$ref: '#/components/schemas/PathTrackCodec' $ref: '#/components/schemas/PathTrackCodec'
inboundBytes:
type: integer
format: uint64
outboundBytes:
type: integer
format: uint64
inboundFramesInError:
type: integer
format: uint64
bytesReceived: bytesReceived:
type: integer type: integer
format: uint64 format: uint64
deprecated: true
bytesSent: bytesSent:
type: integer type: integer
format: uint64 format: uint64
deprecated: true
readers: readers:
type: array type: array
items: items:
@@ -1023,9 +1034,16 @@ components:
type: string type: string
lastRequest: lastRequest:
type: string type: string
outboundBytes:
type: integer
format: uint64
outboundFramesDiscarded:
type: integer
format: uint64
bytesSent: bytesSent:
type: integer type: integer
format: uint64 format: uint64
deprecated: true
HLSMuxerList: HLSMuxerList:
type: object type: object
@@ -1089,12 +1107,23 @@ components:
type: string type: string
user: user:
type: string type: string
inboundBytes:
type: integer
format: uint64
outboundBytes:
type: integer
format: uint64
outboundFramesDiscarded:
type: integer
format: uint64
bytesReceived: bytesReceived:
type: integer type: integer
format: uint64 format: uint64
deprecated: true
bytesSent: bytesSent:
type: integer type: integer
format: uint64 format: uint64
deprecated: true
RTMPConnList: RTMPConnList:
type: object type: object
@@ -1214,6 +1243,9 @@ components:
outboundRTPPacketsReportedLost: outboundRTPPacketsReportedLost:
type: integer type: integer
format: uint64 format: uint64
outboundRTPPacketsDiscarded:
type: integer
format: uint64
outboundRTCPPackets: outboundRTCPPackets:
type: integer type: integer
format: uint64 format: uint64
@@ -1500,6 +1532,9 @@ components:
type: number type: number
format: double format: double
description: Percentage of retransmitted data vs. received data description: Percentage of retransmitted data vs. received data
outboundFramesDiscarded:
type: integer
format: uint64
SRTConnList: SRTConnList:
type: object type: object
@@ -1589,6 +1624,9 @@ components:
outboundRTCPPackets: outboundRTCPPackets:
type: integer type: integer
format: uint64 format: uint64
outboundFramesDiscarded:
type: integer
format: uint64
bytesReceived: bytesReceived:
type: integer type: integer
format: uint64 format: uint64
+20 -12
View File
@@ -37,16 +37,20 @@ func TestHLSMuxersList(t *testing.T) {
hlsServer := &testHLSServer{ hlsServer := &testHLSServer{
muxers: map[string]*defs.APIHLSMuxer{ muxers: map[string]*defs.APIHLSMuxer{
"test1": { "test1": {
Path: "test1", Path: "test1",
Created: now, Created: now,
LastRequest: now.Add(5 * time.Second), LastRequest: now.Add(5 * time.Second),
BytesSent: 1234, OutboundBytes: 1234,
OutboundFramesDiscarded: 10,
BytesSent: 1234,
}, },
"test2": { "test2": {
Path: "test2", Path: "test2",
Created: now.Add(time.Minute), Created: now.Add(time.Minute),
LastRequest: now.Add(time.Minute + 10*time.Second), LastRequest: now.Add(time.Minute + 10*time.Second),
BytesSent: 5678, OutboundBytes: 5678,
OutboundFramesDiscarded: 20,
BytesSent: 5678,
}, },
}, },
} }
@@ -80,10 +84,12 @@ func TestHLSMuxersGet(t *testing.T) {
hlsServer := &testHLSServer{ hlsServer := &testHLSServer{
muxers: map[string]*defs.APIHLSMuxer{ muxers: map[string]*defs.APIHLSMuxer{
"mypath": { "mypath": {
Path: "mypath", Path: "mypath",
Created: now, Created: now,
LastRequest: now.Add(5 * time.Second), LastRequest: now.Add(5 * time.Second),
BytesSent: 9999, OutboundBytes: 9999,
OutboundFramesDiscarded: 12,
BytesSent: 9999,
}, },
}, },
} }
@@ -108,5 +114,7 @@ func TestHLSMuxersGet(t *testing.T) {
httpRequest(t, hc, http.MethodGet, "http://localhost:9997/v3/hlsmuxers/get/mypath", nil, &out) httpRequest(t, hc, http.MethodGet, "http://localhost:9997/v3/hlsmuxers/get/mypath", nil, &out)
require.Equal(t, "mypath", out.Path) require.Equal(t, "mypath", out.Path)
require.Equal(t, uint64(9999), out.OutboundBytes)
require.Equal(t, uint64(12), out.OutboundFramesDiscarded)
require.Equal(t, uint64(9999), out.BytesSent) require.Equal(t, uint64(9999), out.BytesSent)
} }
+35 -23
View File
@@ -36,26 +36,32 @@ func TestPathsList(t *testing.T) {
pathManager := &testPathManager{ pathManager := &testPathManager{
paths: map[string]*defs.APIPath{ paths: map[string]*defs.APIPath{
"test1": { "test1": {
Name: "test1", Name: "test1",
ConfName: "test1", ConfName: "test1",
Source: &defs.APIPathSource{Type: defs.APIPathSourceTypeRTSPSession, ID: "pub1"}, Source: &defs.APIPathSource{Type: defs.APIPathSourceTypeRTSPSession, ID: "pub1"},
Ready: true, Ready: true,
ReadyTime: &now, ReadyTime: &now,
Tracks: []defs.APIPathTrackCodec{defs.APIPathTrackCodecH264, defs.APIPathTrackCodecOpus}, Tracks: []defs.APIPathTrackCodec{defs.APIPathTrackCodecH264, defs.APIPathTrackCodecOpus},
BytesReceived: 1000, InboundBytes: 1000,
BytesSent: 2000, OutboundBytes: 2000,
InboundFramesInError: 3,
BytesReceived: 1000,
BytesSent: 2000,
Readers: []defs.APIPathReader{ Readers: []defs.APIPathReader{
{Type: defs.APIPathReaderTypeRTSPSession, ID: "reader1"}, {Type: defs.APIPathReaderTypeRTSPSession, ID: "reader1"},
}, },
}, },
"test2": { "test2": {
Name: "test2", Name: "test2",
ConfName: "test2", ConfName: "test2",
Ready: false, Ready: false,
Tracks: []defs.APIPathTrackCodec{}, Tracks: []defs.APIPathTrackCodec{},
BytesReceived: 500, InboundBytes: 500,
BytesSent: 100, OutboundBytes: 100,
Readers: []defs.APIPathReader{}, InboundFramesInError: 1,
BytesReceived: 500,
BytesSent: 100,
Readers: []defs.APIPathReader{},
}, },
}, },
} }
@@ -89,14 +95,17 @@ func TestPathsGet(t *testing.T) {
pathManager := &testPathManager{ pathManager := &testPathManager{
paths: map[string]*defs.APIPath{ paths: map[string]*defs.APIPath{
"mystream": { "mystream": {
Name: "mystream", Name: "mystream",
ConfName: "mystream", ConfName: "mystream",
Source: &defs.APIPathSource{Type: defs.APIPathSourceTypeRTSPSession, ID: "session123"}, Source: &defs.APIPathSource{Type: defs.APIPathSourceTypeRTSPSession, ID: "session123"},
Ready: true, Ready: true,
ReadyTime: &now, ReadyTime: &now,
Tracks: []defs.APIPathTrackCodec{defs.APIPathTrackCodecH264, defs.APIPathTrackCodecOpus}, Tracks: []defs.APIPathTrackCodec{defs.APIPathTrackCodecH264, defs.APIPathTrackCodecOpus},
BytesReceived: 123456, InboundBytes: 123456,
BytesSent: 789012, OutboundBytes: 789012,
InboundFramesInError: 12,
BytesReceived: 123456,
BytesSent: 789012,
Readers: []defs.APIPathReader{ Readers: []defs.APIPathReader{
{Type: defs.APIPathReaderTypeHLSMuxer, ID: "muxer1"}, {Type: defs.APIPathReaderTypeHLSMuxer, ID: "muxer1"},
{Type: defs.APIPathReaderTypeWebRTCSession, ID: "session456"}, {Type: defs.APIPathReaderTypeWebRTCSession, ID: "session456"},
@@ -131,6 +140,9 @@ func TestPathsGet(t *testing.T) {
require.Equal(t, defs.APIPathSourceTypeRTSPSession, out.Source.Type) require.Equal(t, defs.APIPathSourceTypeRTSPSession, out.Source.Type)
require.Len(t, out.Tracks, 2) require.Len(t, out.Tracks, 2)
require.Len(t, out.Readers, 2) require.Len(t, out.Readers, 2)
require.Equal(t, uint64(123456), out.InboundBytes)
require.Equal(t, uint64(789012), out.OutboundBytes)
require.Equal(t, uint64(12), out.InboundFramesInError)
require.Equal(t, uint64(123456), out.BytesReceived) require.Equal(t, uint64(123456), out.BytesReceived)
require.Equal(t, uint64(789012), out.BytesSent) require.Equal(t, uint64(789012), out.BytesSent)
} }
+36 -24
View File
@@ -67,24 +67,30 @@ func TestRTMPConnsList(t *testing.T) {
rtmpServer := &testRTMPServer{ rtmpServer := &testRTMPServer{
conns: map[uuid.UUID]*defs.APIRTMPConn{ conns: map[uuid.UUID]*defs.APIRTMPConn{
id1: { id1: {
ID: id1, ID: id1,
Created: now, Created: now,
RemoteAddr: "192.168.1.1:5000", RemoteAddr: "192.168.1.1:5000",
State: defs.APIRTMPConnStatePublish, State: defs.APIRTMPConnStatePublish,
Path: "stream1", Path: "stream1",
Query: "token=abc", Query: "token=abc",
BytesReceived: 1000, InboundBytes: 1000,
BytesSent: 2000, OutboundBytes: 2000,
OutboundFramesDiscarded: 11,
BytesReceived: 1000,
BytesSent: 2000,
}, },
id2: { id2: {
ID: id2, ID: id2,
Created: now.Add(time.Minute), Created: now.Add(time.Minute),
RemoteAddr: "192.168.1.2:5001", RemoteAddr: "192.168.1.2:5001",
State: defs.APIRTMPConnStateRead, State: defs.APIRTMPConnStateRead,
Path: "stream2", Path: "stream2",
Query: "", Query: "",
BytesReceived: 500, InboundBytes: 500,
BytesSent: 1500, OutboundBytes: 1500,
OutboundFramesDiscarded: 22,
BytesReceived: 500,
BytesSent: 1500,
}, },
}, },
} }
@@ -148,14 +154,17 @@ func TestRTMPConnsGet(t *testing.T) {
rtmpServer := &testRTMPServer{ rtmpServer := &testRTMPServer{
conns: map[uuid.UUID]*defs.APIRTMPConn{ conns: map[uuid.UUID]*defs.APIRTMPConn{
id: { id: {
ID: id, ID: id,
Created: now, Created: now,
RemoteAddr: "192.168.1.100:5000", RemoteAddr: "192.168.1.100:5000",
State: defs.APIRTMPConnStatePublish, State: defs.APIRTMPConnStatePublish,
Path: ca.path, Path: ca.path,
Query: "key=value", Query: "key=value",
BytesReceived: 999999, InboundBytes: 999999,
BytesSent: 888888, OutboundBytes: 888888,
OutboundFramesDiscarded: 33,
BytesReceived: 999999,
BytesSent: 888888,
}, },
}, },
} }
@@ -189,6 +198,9 @@ func TestRTMPConnsGet(t *testing.T) {
require.Equal(t, "192.168.1.100:5000", out.RemoteAddr) require.Equal(t, "192.168.1.100:5000", out.RemoteAddr)
require.Equal(t, defs.APIRTMPConnStatePublish, out.State) require.Equal(t, defs.APIRTMPConnStatePublish, out.State)
require.Equal(t, ca.path, out.Path) require.Equal(t, ca.path, out.Path)
require.Equal(t, uint64(999999), out.InboundBytes)
require.Equal(t, uint64(888888), out.OutboundBytes)
require.Equal(t, uint64(33), out.OutboundFramesDiscarded)
require.Equal(t, uint64(999999), out.BytesReceived) require.Equal(t, uint64(999999), out.BytesReceived)
}) })
} }
+34 -30
View File
@@ -50,38 +50,40 @@ func TestSRTConnsList(t *testing.T) {
srtServer := &testSRTServer{ srtServer := &testSRTServer{
conns: map[uuid.UUID]*defs.APISRTConn{ conns: map[uuid.UUID]*defs.APISRTConn{
id1: { id1: {
ID: id1, ID: id1,
Created: now, Created: now,
RemoteAddr: "192.168.1.1:5000", RemoteAddr: "192.168.1.1:5000",
State: defs.APISRTConnStatePublish, State: defs.APISRTConnStatePublish,
Path: "stream1", Path: "stream1",
Query: "token=abc", Query: "token=abc",
PacketsSent: 1000, PacketsSent: 1000,
PacketsReceived: 2000, PacketsReceived: 2000,
PacketsSentUnique: 950, PacketsSentUnique: 950,
PacketsReceivedUnique: 1950, PacketsReceivedUnique: 1950,
BytesReceived: 100000, BytesReceived: 100000,
BytesSent: 200000, BytesSent: 200000,
MsRTT: 10.5, OutboundFramesDiscarded: 5,
MbpsSendRate: 5.2, MsRTT: 10.5,
MbpsReceiveRate: 4.8, MbpsSendRate: 5.2,
MbpsReceiveRate: 4.8,
}, },
id2: { id2: {
ID: id2, ID: id2,
Created: now.Add(time.Minute), Created: now.Add(time.Minute),
RemoteAddr: "192.168.1.2:5001", RemoteAddr: "192.168.1.2:5001",
State: defs.APISRTConnStateRead, State: defs.APISRTConnStateRead,
Path: "stream2", Path: "stream2",
Query: "", Query: "",
PacketsSent: 500, PacketsSent: 500,
PacketsReceived: 1500, PacketsReceived: 1500,
PacketsSentUnique: 480, PacketsSentUnique: 480,
PacketsReceivedUnique: 1470, PacketsReceivedUnique: 1470,
BytesReceived: 50000, BytesReceived: 50000,
BytesSent: 150000, BytesSent: 150000,
MsRTT: 15.2, OutboundFramesDiscarded: 6,
MbpsSendRate: 3.5, MsRTT: 15.2,
MbpsReceiveRate: 3.2, MbpsSendRate: 3.5,
MbpsReceiveRate: 3.2,
}, },
}, },
} }
@@ -176,6 +178,7 @@ func TestSRTConnsGet(t *testing.T) {
PacketsReceivedAvgBelatedTime: 50, PacketsReceivedAvgBelatedTime: 50,
PacketsSendLossRate: 0.5, PacketsSendLossRate: 0.5,
PacketsReceivedLossRate: 0.6, PacketsReceivedLossRate: 0.6,
OutboundFramesDiscarded: 7,
}, },
}, },
} }
@@ -205,6 +208,7 @@ func TestSRTConnsGet(t *testing.T) {
require.Equal(t, "mystream", out.Path) require.Equal(t, "mystream", out.Path)
require.Equal(t, uint64(999999), out.BytesReceived) require.Equal(t, uint64(999999), out.BytesReceived)
require.Equal(t, uint64(888888), out.BytesSent) require.Equal(t, uint64(888888), out.BytesSent)
require.Equal(t, uint64(7), out.OutboundFramesDiscarded)
require.Equal(t, 25.5, out.MsRTT) require.Equal(t, 25.5, out.MsRTT)
require.Equal(t, 10.5, out.MbpsSendRate) require.Equal(t, 10.5, out.MbpsSendRate)
require.Equal(t, 9.8, out.MbpsReceiveRate) require.Equal(t, 9.8, out.MbpsReceiveRate)
+4
View File
@@ -67,6 +67,7 @@ func TestWebRTCSessionsList(t *testing.T) {
OutboundBytes: 2000, OutboundBytes: 2000,
OutboundRTPPackets: 200, OutboundRTPPackets: 200,
OutboundRTCPPackets: 15, OutboundRTCPPackets: 15,
OutboundFramesDiscarded: 11,
BytesReceived: 1000, BytesReceived: 1000,
BytesSent: 2000, BytesSent: 2000,
RTPPacketsReceived: 100, RTPPacketsReceived: 100,
@@ -94,6 +95,7 @@ func TestWebRTCSessionsList(t *testing.T) {
OutboundBytes: 1500, OutboundBytes: 1500,
OutboundRTPPackets: 150, OutboundRTPPackets: 150,
OutboundRTCPPackets: 10, OutboundRTCPPackets: 10,
OutboundFramesDiscarded: 22,
BytesReceived: 500, BytesReceived: 500,
BytesSent: 1500, BytesSent: 1500,
RTPPacketsReceived: 50, RTPPacketsReceived: 50,
@@ -154,6 +156,7 @@ func TestWebRTCSessionsGet(t *testing.T) {
OutboundBytes: 888888, OutboundBytes: 888888,
OutboundRTPPackets: 20000, OutboundRTPPackets: 20000,
OutboundRTCPPackets: 200, OutboundRTCPPackets: 200,
OutboundFramesDiscarded: 33,
BytesReceived: 999999, BytesReceived: 999999,
BytesSent: 888888, BytesSent: 888888,
RTPPacketsReceived: 10000, RTPPacketsReceived: 10000,
@@ -196,6 +199,7 @@ func TestWebRTCSessionsGet(t *testing.T) {
require.Equal(t, uint64(888888), out.OutboundBytes) require.Equal(t, uint64(888888), out.OutboundBytes)
require.Equal(t, uint64(10000), out.InboundRTPPackets) require.Equal(t, uint64(10000), out.InboundRTPPackets)
require.Equal(t, uint64(20000), out.OutboundRTPPackets) require.Equal(t, uint64(20000), out.OutboundRTPPackets)
require.Equal(t, uint64(33), out.OutboundFramesDiscarded)
require.Equal(t, uint64(999999), out.BytesReceived) require.Equal(t, uint64(999999), out.BytesReceived)
require.Equal(t, uint64(888888), out.BytesSent) require.Equal(t, uint64(888888), out.BytesSent)
require.Equal(t, uint64(10000), out.RTPPacketsReceived) require.Equal(t, uint64(10000), out.RTPPacketsReceived)
+62 -37
View File
@@ -80,12 +80,15 @@ func TestAPIPathsList(t *testing.T) {
} }
type path struct { type path struct {
Name string `json:"name"` Name string `json:"name"`
Source pathSource `json:"source"` Source pathSource `json:"source"`
Ready bool `json:"ready"` Ready bool `json:"ready"`
Tracks []defs.APIPathTrackCodec `json:"tracks"` Tracks []defs.APIPathTrackCodec `json:"tracks"`
BytesReceived uint64 `json:"bytesReceived"` InboundBytes uint64 `json:"inboundBytes"`
BytesSent uint64 `json:"bytesSent"` OutboundBytes uint64 `json:"outboundBytes"`
InboundFramesInError uint64 `json:"inboundFramesInError"`
BytesReceived uint64 `json:"bytesReceived"`
BytesSent uint64 `json:"bytesSent"`
} }
type pathList struct { type pathList struct {
@@ -136,9 +139,11 @@ func TestAPIPathsList(t *testing.T) {
Source: pathSource{ Source: pathSource{
Type: "rtspSession", Type: "rtspSession",
}, },
Ready: true, Ready: true,
Tracks: []defs.APIPathTrackCodec{defs.APIPathTrackCodecH264, defs.APIPathTrackCodecMPEG4Audio}, Tracks: []defs.APIPathTrackCodec{defs.APIPathTrackCodecH264, defs.APIPathTrackCodecMPEG4Audio},
BytesReceived: 17, InboundBytes: 17,
InboundFramesInError: 0,
BytesReceived: 17,
}}, }},
}, out) }, out)
}) })
@@ -296,12 +301,15 @@ func TestAPIPathsGet(t *testing.T) {
} }
type path struct { type path struct {
Name string `json:"name"` Name string `json:"name"`
Source pathSource `json:"source"` Source pathSource `json:"source"`
Ready bool `json:"Ready"` Ready bool `json:"Ready"`
Tracks []defs.APIPathTrackCodec `json:"tracks"` Tracks []defs.APIPathTrackCodec `json:"tracks"`
BytesReceived uint64 `json:"bytesReceived"` InboundBytes uint64 `json:"inboundBytes"`
BytesSent uint64 `json:"bytesSent"` OutboundBytes uint64 `json:"outboundBytes"`
InboundFramesInError uint64 `json:"inboundFramesInError"`
BytesReceived uint64 `json:"bytesReceived"`
BytesSent uint64 `json:"bytesSent"`
} }
var pathName string var pathName string
@@ -652,6 +660,7 @@ func TestAPIProtocolListGet(t *testing.T) {
"outboundBytes": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundBytes"], "outboundBytes": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundBytes"],
"outboundRTPPackets": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTPPackets"], "outboundRTPPackets": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTPPackets"],
"outboundRTPPacketsReportedLost": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTPPacketsReportedLost"], "outboundRTPPacketsReportedLost": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTPPacketsReportedLost"],
"outboundRTPPacketsDiscarded": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTPPacketsDiscarded"],
"outboundRTCPPackets": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTCPPackets"], "outboundRTCPPackets": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTCPPackets"],
"bytesReceived": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesReceived"], "bytesReceived": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesReceived"],
"bytesSent": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesSent"], "bytesSent": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesSent"],
@@ -712,6 +721,7 @@ func TestAPIProtocolListGet(t *testing.T) {
"outboundBytes": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundBytes"], "outboundBytes": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundBytes"],
"outboundRTPPackets": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTPPackets"], "outboundRTPPackets": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTPPackets"],
"outboundRTPPacketsReportedLost": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTPPacketsReportedLost"], "outboundRTPPacketsReportedLost": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTPPacketsReportedLost"],
"outboundRTPPacketsDiscarded": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTPPacketsDiscarded"],
"outboundRTCPPackets": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTCPPackets"], "outboundRTCPPackets": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTCPPackets"],
"bytesReceived": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesReceived"], "bytesReceived": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesReceived"],
"bytesSent": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesSent"], "bytesSent": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesSent"],
@@ -743,15 +753,18 @@ func TestAPIProtocolListGet(t *testing.T) {
"itemCount": float64(1), "itemCount": float64(1),
"items": []any{ "items": []any{
map[string]any{ map[string]any{
"bytesReceived": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesReceived"], "inboundBytes": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["inboundBytes"],
"bytesSent": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesSent"], "outboundBytes": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundBytes"],
"created": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["created"], "outboundFramesDiscarded": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundFramesDiscarded"],
"id": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["id"], "bytesReceived": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesReceived"],
"path": "mypath", "bytesSent": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesSent"],
"query": "key=val", "created": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["created"],
"user": "", "id": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["id"],
"remoteAddr": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["remoteAddr"], "path": "mypath",
"state": "publish", "query": "key=val",
"user": "",
"remoteAddr": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["remoteAddr"],
"state": "publish",
}, },
}, },
}, out1) }, out1)
@@ -762,15 +775,18 @@ func TestAPIProtocolListGet(t *testing.T) {
"itemCount": float64(1), "itemCount": float64(1),
"items": []any{ "items": []any{
map[string]any{ map[string]any{
"bytesReceived": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesReceived"], "inboundBytes": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["inboundBytes"],
"bytesSent": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesSent"], "outboundBytes": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundBytes"],
"created": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["created"], "outboundFramesDiscarded": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundFramesDiscarded"],
"id": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["id"], "bytesReceived": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesReceived"],
"path": "mypath", "bytesSent": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesSent"],
"query": "key=val", "created": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["created"],
"user": "", "id": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["id"],
"remoteAddr": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["remoteAddr"], "path": "mypath",
"state": "publish", "query": "key=val",
"user": "",
"remoteAddr": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["remoteAddr"],
"state": "publish",
}, },
}, },
}, out1) }, out1)
@@ -781,10 +797,12 @@ func TestAPIProtocolListGet(t *testing.T) {
"pageCount": float64(1), "pageCount": float64(1),
"items": []any{ "items": []any{
map[string]any{ map[string]any{
"bytesSent": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesSent"], "outboundBytes": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundBytes"],
"created": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["created"], "outboundFramesDiscarded": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundFramesDiscarded"],
"lastRequest": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["lastRequest"], "bytesSent": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesSent"],
"path": "mypath", "created": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["created"],
"lastRequest": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["lastRequest"],
"path": "mypath",
}, },
}, },
}, out1) }, out1)
@@ -803,6 +821,7 @@ func TestAPIProtocolListGet(t *testing.T) {
"outboundBytes": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundBytes"], "outboundBytes": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundBytes"],
"outboundRTPPackets": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTPPackets"], "outboundRTPPackets": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTPPackets"],
"outboundRTCPPackets": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTCPPackets"], "outboundRTCPPackets": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTCPPackets"],
"outboundFramesDiscarded": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundFramesDiscarded"],
"bytesReceived": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesReceived"], "bytesReceived": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesReceived"],
"bytesSent": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesSent"], "bytesSent": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesSent"],
"created": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["created"], "created": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["created"],
@@ -841,6 +860,7 @@ func TestAPIProtocolListGet(t *testing.T) {
"bytesReceivedLoss": float64(0), "bytesReceivedLoss": float64(0),
"bytesReceivedRetrans": float64(0), "bytesReceivedRetrans": float64(0),
"bytesReceivedUndecrypt": float64(0), "bytesReceivedUndecrypt": float64(0),
"outboundFramesDiscarded": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundFramesDiscarded"],
"bytesReceivedUnique": float64(628), "bytesReceivedUnique": float64(628),
"bytesRetrans": float64(0), "bytesRetrans": float64(0),
"bytesSendBuf": float64(0), "bytesSendBuf": float64(0),
@@ -926,6 +946,7 @@ func TestAPIProtocolListGet(t *testing.T) {
out2.(map[string]any)["outboundBytes"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundBytes"] out2.(map[string]any)["outboundBytes"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundBytes"]
out2.(map[string]any)["outboundRTPPackets"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTPPackets"] out2.(map[string]any)["outboundRTPPackets"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTPPackets"]
out2.(map[string]any)["outboundRTPPacketsReportedLost"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTPPacketsReportedLost"] out2.(map[string]any)["outboundRTPPacketsReportedLost"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTPPacketsReportedLost"]
out2.(map[string]any)["outboundRTPPacketsDiscarded"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTPPacketsDiscarded"]
out2.(map[string]any)["outboundRTCPPackets"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTCPPackets"] out2.(map[string]any)["outboundRTCPPackets"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTCPPackets"]
out2.(map[string]any)["bytesReceived"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesReceived"] out2.(map[string]any)["bytesReceived"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesReceived"]
out2.(map[string]any)["bytesSent"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesSent"] out2.(map[string]any)["bytesSent"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesSent"]
@@ -947,6 +968,7 @@ func TestAPIProtocolListGet(t *testing.T) {
out2.(map[string]any)["outboundBytes"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundBytes"] out2.(map[string]any)["outboundBytes"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundBytes"]
out2.(map[string]any)["outboundRTPPackets"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTPPackets"] out2.(map[string]any)["outboundRTPPackets"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTPPackets"]
out2.(map[string]any)["outboundRTCPPackets"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTCPPackets"] out2.(map[string]any)["outboundRTCPPackets"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTCPPackets"]
out2.(map[string]any)["outboundFramesDiscarded"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundFramesDiscarded"]
out2.(map[string]any)["bytesReceived"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesReceived"] out2.(map[string]any)["bytesReceived"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesReceived"]
out2.(map[string]any)["bytesSent"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesSent"] out2.(map[string]any)["bytesSent"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesSent"]
out2.(map[string]any)["rtpPacketsReceived"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["rtpPacketsReceived"] out2.(map[string]any)["rtpPacketsReceived"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["rtpPacketsReceived"]
@@ -955,6 +977,9 @@ func TestAPIProtocolListGet(t *testing.T) {
out2.(map[string]any)["rtpPacketsJitter"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["rtpPacketsJitter"] out2.(map[string]any)["rtpPacketsJitter"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["rtpPacketsJitter"]
out2.(map[string]any)["rtcpPacketsReceived"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["rtcpPacketsReceived"] out2.(map[string]any)["rtcpPacketsReceived"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["rtcpPacketsReceived"]
out2.(map[string]any)["rtcpPacketsSent"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["rtcpPacketsSent"] out2.(map[string]any)["rtcpPacketsSent"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["rtcpPacketsSent"]
case "hls":
out2.(map[string]any)["lastRequest"] = out1.(map[string]any)["items"].([]any)[0].(map[string]any)["lastRequest"]
} }
require.Equal(t, out1.(map[string]any)["items"].([]any)[0], out2) require.Equal(t, out1.(map[string]any)["items"].([]any)[0], out2)
+60
View File
@@ -76,10 +76,15 @@ func TestMetrics(t *testing.T) {
bo := httpPullFile(t, hc, "http://localhost:9998/metrics") bo := httpPullFile(t, hc, "http://localhost:9998/metrics")
require.Equal(t, `paths 0 require.Equal(t, `paths 0
paths_inbound_bytes 0
paths_outbound_bytes 0
paths_inbound_frames_in_error 0
paths_bytes_received 0 paths_bytes_received 0
paths_bytes_sent 0 paths_bytes_sent 0
paths_readers 0 paths_readers 0
hls_muxers 0 hls_muxers 0
hls_muxers_outbound_bytes 0
hls_muxers_outbound_frames_discarded 0
hls_muxers_bytes_sent 0 hls_muxers_bytes_sent 0
rtsp_conns 0 rtsp_conns 0
rtsp_conns_inbound_bytes 0 rtsp_conns_inbound_bytes 0
@@ -97,6 +102,7 @@ rtsp_sessions_inbound_rtcp_packets_in_error 0
rtsp_sessions_outbound_bytes 0 rtsp_sessions_outbound_bytes 0
rtsp_sessions_outbound_rtp_packets 0 rtsp_sessions_outbound_rtp_packets 0
rtsp_sessions_outbound_rtp_packets_reported_lost 0 rtsp_sessions_outbound_rtp_packets_reported_lost 0
rtsp_sessions_outbound_rtp_packets_discarded 0
rtsp_sessions_outbound_rtcp_packets 0 rtsp_sessions_outbound_rtcp_packets 0
rtsp_sessions_bytes_received 0 rtsp_sessions_bytes_received 0
rtsp_sessions_bytes_sent 0 rtsp_sessions_bytes_sent 0
@@ -124,6 +130,7 @@ rtsps_sessions_inbound_rtcp_packets_in_error 0
rtsps_sessions_outbound_bytes 0 rtsps_sessions_outbound_bytes 0
rtsps_sessions_outbound_rtp_packets 0 rtsps_sessions_outbound_rtp_packets 0
rtsps_sessions_outbound_rtp_packets_reported_lost 0 rtsps_sessions_outbound_rtp_packets_reported_lost 0
rtsps_sessions_outbound_rtp_packets_discarded 0
rtsps_sessions_outbound_rtcp_packets 0 rtsps_sessions_outbound_rtcp_packets 0
rtsps_sessions_bytes_received 0 rtsps_sessions_bytes_received 0
rtsps_sessions_bytes_sent 0 rtsps_sessions_bytes_sent 0
@@ -136,9 +143,15 @@ rtsps_sessions_rtcp_packets_received 0
rtsps_sessions_rtcp_packets_sent 0 rtsps_sessions_rtcp_packets_sent 0
rtsps_sessions_rtcp_packets_in_error 0 rtsps_sessions_rtcp_packets_in_error 0
rtmp_conns 0 rtmp_conns 0
rtmp_conns_inbound_bytes 0
rtmp_conns_outbound_bytes 0
rtmp_conns_outbound_frames_discarded 0
rtmp_conns_bytes_received 0 rtmp_conns_bytes_received 0
rtmp_conns_bytes_sent 0 rtmp_conns_bytes_sent 0
rtmps_conns 0 rtmps_conns 0
rtmps_conns_inbound_bytes 0
rtmps_conns_outbound_bytes 0
rtmps_conns_outbound_frames_discarded 0
rtmps_conns_bytes_received 0 rtmps_conns_bytes_received 0
rtmps_conns_bytes_sent 0 rtmps_conns_bytes_sent 0
srt_conns 0 srt_conns 0
@@ -195,6 +208,7 @@ srt_conns_packets_reorder_tolerance 0
srt_conns_packets_received_avg_belated_time 0 srt_conns_packets_received_avg_belated_time 0
srt_conns_packets_send_loss_rate 0 srt_conns_packets_send_loss_rate 0
srt_conns_packets_received_loss_rate 0 srt_conns_packets_received_loss_rate 0
srt_conns_outbound_frames_discarded 0
webrtc_sessions 0 webrtc_sessions 0
webrtc_sessions_inbound_bytes 0 webrtc_sessions_inbound_bytes 0
webrtc_sessions_inbound_rtp_packets 0 webrtc_sessions_inbound_rtp_packets 0
@@ -204,6 +218,7 @@ webrtc_sessions_inbound_rtcp_packets 0
webrtc_sessions_outbound_bytes 0 webrtc_sessions_outbound_bytes 0
webrtc_sessions_outbound_rtp_packets 0 webrtc_sessions_outbound_rtp_packets 0
webrtc_sessions_outbound_rtcp_packets 0 webrtc_sessions_outbound_rtcp_packets 0
webrtc_sessions_outbound_frames_discarded 0
webrtc_sessions_bytes_received 0 webrtc_sessions_bytes_received 0
webrtc_sessions_bytes_sent 0 webrtc_sessions_bytes_sent 0
webrtc_sessions_rtp_packets_received 0 webrtc_sessions_rtp_packets_received 0
@@ -395,40 +410,70 @@ webrtc_sessions_rtcp_packets_sent 0
require.Regexp(t, require.Regexp(t,
`^paths\{name=".*?",state="ready"\} 1`+"\n"+ `^paths\{name=".*?",state="ready"\} 1`+"\n"+
`paths_inbound_bytes\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_outbound_bytes\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_inbound_frames_in_error\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_bytes_received\{name=".*?",state="ready"\} [0-9]+`+"\n"+ `paths_bytes_received\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_bytes_sent\{name=".*?",state="ready"\} [0-9]+`+"\n"+ `paths_bytes_sent\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_readers\{name=".*?",state="ready"\} [0-9]+`+"\n"+ `paths_readers\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths\{name=".*?",state="ready"\} 1`+"\n"+ `paths\{name=".*?",state="ready"\} 1`+"\n"+
`paths_inbound_bytes\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_outbound_bytes\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_inbound_frames_in_error\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_bytes_received\{name=".*?",state="ready"\} [0-9]+`+"\n"+ `paths_bytes_received\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_bytes_sent\{name=".*?",state="ready"\} [0-9]+`+"\n"+ `paths_bytes_sent\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_readers\{name=".*?",state="ready"\} [0-9]+`+"\n"+ `paths_readers\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths\{name=".*?",state="ready"\} 1`+"\n"+ `paths\{name=".*?",state="ready"\} 1`+"\n"+
`paths_inbound_bytes\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_outbound_bytes\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_inbound_frames_in_error\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_bytes_received\{name=".*?",state="ready"\} [0-9]+`+"\n"+ `paths_bytes_received\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_bytes_sent\{name=".*?",state="ready"\} [0-9]+`+"\n"+ `paths_bytes_sent\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_readers\{name=".*?",state="ready"\} [0-9]+`+"\n"+ `paths_readers\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths\{name=".*?",state="ready"\} 1`+"\n"+ `paths\{name=".*?",state="ready"\} 1`+"\n"+
`paths_inbound_bytes\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_outbound_bytes\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_inbound_frames_in_error\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_bytes_received\{name=".*?",state="ready"\} [0-9]+`+"\n"+ `paths_bytes_received\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_bytes_sent\{name=".*?",state="ready"\} [0-9]+`+"\n"+ `paths_bytes_sent\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_readers\{name=".*?",state="ready"\} [0-9]+`+"\n"+ `paths_readers\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths\{name=".*?",state="ready"\} 1`+"\n"+ `paths\{name=".*?",state="ready"\} 1`+"\n"+
`paths_inbound_bytes\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_outbound_bytes\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_inbound_frames_in_error\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_bytes_received\{name=".*?",state="ready"\} [0-9]+`+"\n"+ `paths_bytes_received\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_bytes_sent\{name=".*?",state="ready"\} [0-9]+`+"\n"+ `paths_bytes_sent\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_readers\{name=".*?",state="ready"\} [0-9]+`+"\n"+ `paths_readers\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths\{name=".*?",state="ready"\} 1`+"\n"+ `paths\{name=".*?",state="ready"\} 1`+"\n"+
`paths_inbound_bytes\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_outbound_bytes\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_inbound_frames_in_error\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_bytes_received\{name=".*?",state="ready"\} [0-9]+`+"\n"+ `paths_bytes_received\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_bytes_sent\{name=".*?",state="ready"\} [0-9]+`+"\n"+ `paths_bytes_sent\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_readers\{name=".*?",state="ready"\} [0-9]+`+"\n"+ `paths_readers\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`hls_muxers\{name=".*?"\} 1`+"\n"+ `hls_muxers\{name=".*?"\} 1`+"\n"+
`hls_muxers_outbound_bytes\{name=".*?"\} 0`+"\n"+
`hls_muxers_outbound_frames_discarded\{name=".*?"\} 0`+"\n"+
`hls_muxers_bytes_sent\{name=".*?"\} 0`+"\n"+ `hls_muxers_bytes_sent\{name=".*?"\} 0`+"\n"+
`hls_muxers\{name=".*?"\} 1`+"\n"+ `hls_muxers\{name=".*?"\} 1`+"\n"+
`hls_muxers_outbound_bytes\{name=".*?"\} 0`+"\n"+
`hls_muxers_outbound_frames_discarded\{name=".*?"\} 0`+"\n"+
`hls_muxers_bytes_sent\{name=".*?"\} 0`+"\n"+ `hls_muxers_bytes_sent\{name=".*?"\} 0`+"\n"+
`hls_muxers\{name=".*?"\} 1`+"\n"+ `hls_muxers\{name=".*?"\} 1`+"\n"+
`hls_muxers_outbound_bytes\{name=".*?"\} 0`+"\n"+
`hls_muxers_outbound_frames_discarded\{name=".*?"\} 0`+"\n"+
`hls_muxers_bytes_sent\{name=".*?"\} 0`+"\n"+ `hls_muxers_bytes_sent\{name=".*?"\} 0`+"\n"+
`hls_muxers\{name=".*?"\} 1`+"\n"+ `hls_muxers\{name=".*?"\} 1`+"\n"+
`hls_muxers_outbound_bytes\{name=".*?"\} 0`+"\n"+
`hls_muxers_outbound_frames_discarded\{name=".*?"\} 0`+"\n"+
`hls_muxers_bytes_sent\{name=".*?"\} 0`+"\n"+ `hls_muxers_bytes_sent\{name=".*?"\} 0`+"\n"+
`hls_muxers\{name=".*?"\} 1`+"\n"+ `hls_muxers\{name=".*?"\} 1`+"\n"+
`hls_muxers_outbound_bytes\{name=".*?"\} 0`+"\n"+
`hls_muxers_outbound_frames_discarded\{name=".*?"\} 0`+"\n"+
`hls_muxers_bytes_sent\{name=".*?"\} 0`+"\n"+ `hls_muxers_bytes_sent\{name=".*?"\} 0`+"\n"+
`hls_muxers\{name=".*?"\} 1`+"\n"+ `hls_muxers\{name=".*?"\} 1`+"\n"+
`hls_muxers_outbound_bytes\{name=".*?"\} 0`+"\n"+
`hls_muxers_outbound_frames_discarded\{name=".*?"\} 0`+"\n"+
`hls_muxers_bytes_sent\{name=".*?"\} 0`+"\n"+ `hls_muxers_bytes_sent\{name=".*?"\} 0`+"\n"+
`rtsp_conns\{id=".*?"\} 1`+"\n"+ `rtsp_conns\{id=".*?"\} 1`+"\n"+
`rtsp_conns_inbound_bytes\{id=".*?"\} [0-9]+`+"\n"+ `rtsp_conns_inbound_bytes\{id=".*?"\} [0-9]+`+"\n"+
@@ -447,6 +492,8 @@ webrtc_sessions_rtcp_packets_sent 0
`rtsp_sessions_outbound_rtp_packets\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+ `rtsp_sessions_outbound_rtp_packets\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
`rtsp_sessions_outbound_rtp_packets_reported_lost\{id=".*?",path=".*?",`+ `rtsp_sessions_outbound_rtp_packets_reported_lost\{id=".*?",path=".*?",`+
`remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+ `remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
`rtsp_sessions_outbound_rtp_packets_discarded\{id=".*?",path=".*?",`+
`remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
`rtsp_sessions_outbound_rtcp_packets\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+ `rtsp_sessions_outbound_rtcp_packets\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
`rtsp_sessions_bytes_received\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} 0`+"\n"+ `rtsp_sessions_bytes_received\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} 0`+"\n"+
`rtsp_sessions_bytes_sent\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+ `rtsp_sessions_bytes_sent\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
@@ -475,6 +522,8 @@ webrtc_sessions_rtcp_packets_sent 0
`rtsps_sessions_outbound_rtp_packets\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+ `rtsps_sessions_outbound_rtp_packets\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
`rtsps_sessions_outbound_rtp_packets_reported_lost\{id=".*?",path=".*?",`+ `rtsps_sessions_outbound_rtp_packets_reported_lost\{id=".*?",path=".*?",`+
`remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+ `remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
`rtsps_sessions_outbound_rtp_packets_discarded\{id=".*?",path=".*?",`+
`remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
`rtsps_sessions_outbound_rtcp_packets\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+ `rtsps_sessions_outbound_rtcp_packets\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
`rtsps_sessions_bytes_received\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} 0`+"\n"+ `rtsps_sessions_bytes_received\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} 0`+"\n"+
`rtsps_sessions_bytes_sent\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+ `rtsps_sessions_bytes_sent\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
@@ -487,9 +536,15 @@ webrtc_sessions_rtcp_packets_sent 0
`rtsps_sessions_rtcp_packets_sent\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+ `rtsps_sessions_rtcp_packets_sent\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
`rtsps_sessions_rtcp_packets_in_error\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+ `rtsps_sessions_rtcp_packets_in_error\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
`rtmp_conns\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} 1`+"\n"+ `rtmp_conns\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} 1`+"\n"+
`rtmp_conns_inbound_bytes\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
`rtmp_conns_outbound_bytes\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
`rtmp_conns_outbound_frames_discarded\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
`rtmp_conns_bytes_received\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+ `rtmp_conns_bytes_received\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
`rtmp_conns_bytes_sent\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+ `rtmp_conns_bytes_sent\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
`rtmps_conns\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} 1`+"\n"+ `rtmps_conns\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} 1`+"\n"+
`rtmps_conns_inbound_bytes\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
`rtmps_conns_outbound_bytes\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
`rtmps_conns_outbound_frames_discarded\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
`rtmps_conns_bytes_received\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+ `rtmps_conns_bytes_received\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
`rtmps_conns_bytes_sent\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+ `rtmps_conns_bytes_sent\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
`srt_conns\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} 1`+"\n"+ `srt_conns\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} 1`+"\n"+
@@ -546,6 +601,7 @@ webrtc_sessions_rtcp_packets_sent 0
`srt_conns_packets_received_avg_belated_time\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+ `srt_conns_packets_received_avg_belated_time\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
`srt_conns_packets_send_loss_rate\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+ `srt_conns_packets_send_loss_rate\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
`srt_conns_packets_received_loss_rate\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+ `srt_conns_packets_received_loss_rate\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
`srt_conns_outbound_frames_discarded\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
`webrtc_sessions\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} 1`+"\n"+ `webrtc_sessions\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} 1`+"\n"+
`webrtc_sessions_inbound_bytes\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+ `webrtc_sessions_inbound_bytes\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
`webrtc_sessions_inbound_rtp_packets\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+ `webrtc_sessions_inbound_rtp_packets\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
@@ -555,6 +611,7 @@ webrtc_sessions_rtcp_packets_sent 0
`webrtc_sessions_outbound_bytes\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+ `webrtc_sessions_outbound_bytes\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
`webrtc_sessions_outbound_rtp_packets\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+ `webrtc_sessions_outbound_rtp_packets\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
`webrtc_sessions_outbound_rtcp_packets\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+ `webrtc_sessions_outbound_rtcp_packets\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
`webrtc_sessions_outbound_frames_discarded\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
`webrtc_sessions_bytes_received\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+ `webrtc_sessions_bytes_received\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
`webrtc_sessions_bytes_sent\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+ `webrtc_sessions_bytes_sent\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
`webrtc_sessions_rtp_packets_received\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+ `webrtc_sessions_rtp_packets_received\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\n"+
@@ -584,6 +641,9 @@ webrtc_sessions_rtcp_packets_sent 0
bo := httpPullFile(t, hc, "http://localhost:9998/metrics") bo := httpPullFile(t, hc, "http://localhost:9998/metrics")
require.Equal(t, "paths 0\n"+ require.Equal(t, "paths 0\n"+
"paths_inbound_bytes 0\n"+
"paths_outbound_bytes 0\n"+
"paths_inbound_frames_in_error 0\n"+
"paths_bytes_received 0\n"+ "paths_bytes_received 0\n"+
"paths_bytes_sent 0\n"+ "paths_bytes_sent 0\n"+
"paths_readers 0\n", "paths_readers 0\n",
+18
View File
@@ -661,6 +661,24 @@ func (pa *path) doAPIPathsGet(req pathAPIPathsGetReq) {
} }
return defs.MediasToCodecs(pa.stream.Desc.Medias) return defs.MediasToCodecs(pa.stream.Desc.Medias)
}(), }(),
InboundBytes: func() uint64 {
if !pa.isAvailable() {
return 0
}
return pa.stream.InboundBytes()
}(),
OutboundBytes: func() uint64 {
if !pa.isAvailable() {
return 0
}
return pa.stream.OutboundBytes()
}(),
InboundFramesInError: func() uint64 {
if !pa.isAvailable() {
return 0
}
return pa.stream.InboundFramesInError()
}(),
BytesReceived: func() uint64 { BytesReceived: func() uint64 {
if !pa.isAvailable() { if !pa.isAvailable() {
return 0 return 0
+24 -6
View File
@@ -2,7 +2,7 @@
package counterdumper package counterdumper
import ( import (
"sync/atomic" "sync"
"time" "time"
) )
@@ -14,7 +14,9 @@ const (
type Dumper struct { type Dumper struct {
OnReport func(v uint64) OnReport func(v uint64)
counter *uint64 mutex sync.Mutex
counter uint64
absCounter uint64
terminate chan struct{} terminate chan struct{}
done chan struct{} done chan struct{}
@@ -22,7 +24,6 @@ type Dumper struct {
// Start starts the counter. // Start starts the counter.
func (c *Dumper) Start() { func (c *Dumper) Start() {
c.counter = new(uint64)
c.terminate = make(chan struct{}) c.terminate = make(chan struct{})
c.done = make(chan struct{}) c.done = make(chan struct{})
@@ -37,12 +38,25 @@ func (c *Dumper) Stop() {
// Increase increases the counter value by 1. // Increase increases the counter value by 1.
func (c *Dumper) Increase() { func (c *Dumper) Increase() {
atomic.AddUint64(c.counter, 1) c.mutex.Lock()
defer c.mutex.Unlock()
c.counter++
c.absCounter++
} }
// Add adds value to the counter. // Add adds value to the counter.
func (c *Dumper) Add(v uint64) { func (c *Dumper) Add(v uint64) {
atomic.AddUint64(c.counter, v) c.mutex.Lock()
defer c.mutex.Unlock()
c.counter += v
c.absCounter += v
}
// Get returns the counter value.
func (c *Dumper) Get() uint64 {
c.mutex.Lock()
defer c.mutex.Unlock()
return c.absCounter
} }
func (c *Dumper) run() { func (c *Dumper) run() {
@@ -57,7 +71,11 @@ func (c *Dumper) run() {
return return
case <-t.C: case <-t.C:
v := atomic.SwapUint64(c.counter, 0) c.mutex.Lock()
var v uint64
v, c.counter = c.counter, 0
c.mutex.Unlock()
if v != 0 { if v != 0 {
c.OnReport(v) c.OnReport(v)
} }
+7 -4
View File
@@ -10,10 +10,13 @@ type APIHLSServer interface {
// APIHLSMuxer is an HLS muxer. // APIHLSMuxer is an HLS muxer.
type APIHLSMuxer struct { type APIHLSMuxer struct {
Path string `json:"path"` Path string `json:"path"`
Created time.Time `json:"created"` Created time.Time `json:"created"`
LastRequest time.Time `json:"lastRequest"` LastRequest time.Time `json:"lastRequest"`
BytesSent uint64 `json:"bytesSent"` OutboundBytes uint64 `json:"outboundBytes"`
OutboundFramesDiscarded uint64 `json:"outboundFramesDiscarded"`
// deprecated
BytesSent uint64 `json:"bytesSent" deprecated:"true"`
} }
// APIHLSMuxerList is a list of HLS muxers. // APIHLSMuxerList is a list of HLS muxers.
+17 -13
View File
@@ -63,19 +63,23 @@ type APIPathReader struct {
// APIPath is a path. // APIPath is a path.
type APIPath struct { type APIPath struct {
Name string `json:"name"` Name string `json:"name"`
ConfName string `json:"confName"` ConfName string `json:"confName"`
Ready bool `json:"ready" deprecated:"true"` Ready bool `json:"ready" deprecated:"true"`
ReadyTime *time.Time `json:"readyTime" deprecated:"true"` ReadyTime *time.Time `json:"readyTime" deprecated:"true"`
Available bool `json:"available"` Available bool `json:"available"`
AvailableTime *time.Time `json:"availableTime"` AvailableTime *time.Time `json:"availableTime"`
Online bool `json:"online"` Online bool `json:"online"`
OnlineTime *time.Time `json:"onlineTime"` OnlineTime *time.Time `json:"onlineTime"`
Source *APIPathSource `json:"source"` Source *APIPathSource `json:"source"`
Tracks []APIPathTrackCodec `json:"tracks"` Tracks []APIPathTrackCodec `json:"tracks"`
BytesReceived uint64 `json:"bytesReceived"` Readers []APIPathReader `json:"readers"`
BytesSent uint64 `json:"bytesSent"` InboundBytes uint64 `json:"inboundBytes"`
Readers []APIPathReader `json:"readers"` OutboundBytes uint64 `json:"outboundBytes"`
InboundFramesInError uint64 `json:"inboundFramesInError"`
// deprecated
BytesReceived uint64 `json:"bytesReceived" deprecated:"true"`
BytesSent uint64 `json:"bytesSent" deprecated:"true"`
} }
// APIPathList is a list of paths. // APIPathList is a list of paths.
+13 -9
View File
@@ -25,15 +25,19 @@ const (
// APIRTMPConn is a RTMP connection. // APIRTMPConn is a RTMP connection.
type APIRTMPConn struct { type APIRTMPConn struct {
ID uuid.UUID `json:"id"` ID uuid.UUID `json:"id"`
Created time.Time `json:"created"` Created time.Time `json:"created"`
RemoteAddr string `json:"remoteAddr"` RemoteAddr string `json:"remoteAddr"`
State APIRTMPConnState `json:"state"` State APIRTMPConnState `json:"state"`
Path string `json:"path"` Path string `json:"path"`
Query string `json:"query"` Query string `json:"query"`
User string `json:"user"` User string `json:"user"`
BytesReceived uint64 `json:"bytesReceived"` InboundBytes uint64 `json:"inboundBytes"`
BytesSent uint64 `json:"bytesSent"` OutboundBytes uint64 `json:"outboundBytes"`
OutboundFramesDiscarded uint64 `json:"outboundFramesDiscarded"`
// deprecated
BytesReceived uint64 `json:"bytesReceived" deprecated:"true"`
BytesSent uint64 `json:"bytesSent" deprecated:"true"`
} }
// APIRTMPConnList is a list of RTMP connections. // APIRTMPConnList is a list of RTMP connections.
+1
View File
@@ -67,6 +67,7 @@ type APIRTSPSession struct {
OutboundBytes uint64 `json:"outboundBytes"` OutboundBytes uint64 `json:"outboundBytes"`
OutboundRTPPackets uint64 `json:"outboundRTPPackets"` OutboundRTPPackets uint64 `json:"outboundRTPPackets"`
OutboundRTPPacketsReportedLost uint64 `json:"outboundRTPPacketsReportedLost"` OutboundRTPPacketsReportedLost uint64 `json:"outboundRTPPacketsReportedLost"`
OutboundRTPPacketsDiscarded uint64 `json:"outboundRTPPacketsDiscarded"`
OutboundRTCPPackets uint64 `json:"outboundRTCPPackets"` OutboundRTCPPackets uint64 `json:"outboundRTCPPackets"`
// deprecated // deprecated
BytesReceived uint64 `json:"bytesReceived" deprecated:"true"` BytesReceived uint64 `json:"bytesReceived" deprecated:"true"`
+2
View File
@@ -153,6 +153,8 @@ type APISRTConn struct {
PacketsSendLossRate float64 `json:"packetsSendLossRate"` PacketsSendLossRate float64 `json:"packetsSendLossRate"`
// Percentage of retransmitted data vs. received data // Percentage of retransmitted data vs. received data
PacketsReceivedLossRate float64 `json:"packetsReceivedLossRate"` PacketsReceivedLossRate float64 `json:"packetsReceivedLossRate"`
OutboundFramesDiscarded uint64 `json:"outboundFramesDiscarded"`
} }
// APISRTConnList is a list of SRT connections. // APISRTConnList is a list of SRT connections.
+1
View File
@@ -42,6 +42,7 @@ type APIWebRTCSession struct {
OutboundBytes uint64 `json:"outboundBytes"` OutboundBytes uint64 `json:"outboundBytes"`
OutboundRTPPackets uint64 `json:"outboundRTPPackets"` OutboundRTPPackets uint64 `json:"outboundRTPPackets"`
OutboundRTCPPackets uint64 `json:"outboundRTCPPackets"` OutboundRTCPPackets uint64 `json:"outboundRTCPPackets"`
OutboundFramesDiscarded uint64 `json:"outboundFramesDiscarded"`
// deprecated // deprecated
BytesReceived uint64 `json:"bytesReceived" deprecated:"true"` BytesReceived uint64 `json:"bytesReceived" deprecated:"true"`
BytesSent uint64 `json:"bytesSent" deprecated:"true"` BytesSent uint64 `json:"bytesSent" deprecated:"true"`
+12 -3
View File
@@ -14,9 +14,10 @@ const (
type Dumper struct { type Dumper struct {
OnReport func(v uint64, last error) OnReport func(v uint64, last error)
mutex sync.Mutex mutex sync.Mutex
counter uint64 counter uint64
last error absCounter uint64
last error
terminate chan struct{} terminate chan struct{}
done chan struct{} done chan struct{}
@@ -41,9 +42,17 @@ func (c *Dumper) Add(err error) {
c.mutex.Lock() c.mutex.Lock()
defer c.mutex.Unlock() defer c.mutex.Unlock()
c.counter++ c.counter++
c.absCounter++
c.last = err c.last = err
} }
// Get returns the total number of errors.
func (c *Dumper) Get() uint64 {
c.mutex.Lock()
defer c.mutex.Unlock()
return c.absCounter
}
func (c *Dumper) run() { func (c *Dumper) run() {
defer close(c.done) defer close(c.done)
+38
View File
@@ -223,6 +223,10 @@ func (m *Metrics) onMetrics(ctx *gin.Context) {
"state": state, "state": state,
}) })
out += metric("paths", ta, 1) out += metric("paths", ta, 1)
out += metric("paths_inbound_bytes", ta, int64(i.InboundBytes))
out += metric("paths_outbound_bytes", ta, int64(i.OutboundBytes))
out += metric("paths_inbound_frames_in_error", ta, int64(i.InboundFramesInError))
// deprecated
out += metric("paths_bytes_received", ta, int64(i.BytesReceived)) out += metric("paths_bytes_received", ta, int64(i.BytesReceived))
out += metric("paths_bytes_sent", ta, int64(i.BytesSent)) out += metric("paths_bytes_sent", ta, int64(i.BytesSent))
out += metric("paths_readers", ta, int64(len(i.Readers))) out += metric("paths_readers", ta, int64(len(i.Readers)))
@@ -230,6 +234,10 @@ func (m *Metrics) onMetrics(ctx *gin.Context) {
} }
} else if pathFilter == "" { } else if pathFilter == "" {
out += metric("paths", "", 0) out += metric("paths", "", 0)
out += metric("paths_inbound_bytes", "", 0)
out += metric("paths_outbound_bytes", "", 0)
out += metric("paths_inbound_frames_in_error", "", 0)
// deprecated
out += metric("paths_bytes_received", "", 0) out += metric("paths_bytes_received", "", 0)
out += metric("paths_bytes_sent", "", 0) out += metric("paths_bytes_sent", "", 0)
out += metric("paths_readers", "", 0) out += metric("paths_readers", "", 0)
@@ -248,11 +256,17 @@ func (m *Metrics) onMetrics(ctx *gin.Context) {
"name": i.Path, "name": i.Path,
}) })
out += metric("hls_muxers", ta, 1) out += metric("hls_muxers", ta, 1)
out += metric("hls_muxers_outbound_bytes", ta, int64(i.OutboundBytes))
out += metric("hls_muxers_outbound_frames_discarded", ta, int64(i.OutboundFramesDiscarded))
// deprecated
out += metric("hls_muxers_bytes_sent", ta, int64(i.BytesSent)) out += metric("hls_muxers_bytes_sent", ta, int64(i.BytesSent))
} }
} }
} else if hlsMuxerFilter == "" { } else if hlsMuxerFilter == "" {
out += metric("hls_muxers", "", 0) out += metric("hls_muxers", "", 0)
out += metric("hls_muxers_outbound_bytes", "", 0)
out += metric("hls_muxers_outbound_frames_discarded", "", 0)
// deprecated
out += metric("hls_muxers_bytes_sent", "", 0) out += metric("hls_muxers_bytes_sent", "", 0)
} }
} }
@@ -308,6 +322,7 @@ func (m *Metrics) onMetrics(ctx *gin.Context) {
out += metric("rtsp_sessions_outbound_bytes", ta, int64(i.OutboundBytes)) out += metric("rtsp_sessions_outbound_bytes", ta, int64(i.OutboundBytes))
out += metric("rtsp_sessions_outbound_rtp_packets", ta, int64(i.OutboundRTPPackets)) out += metric("rtsp_sessions_outbound_rtp_packets", ta, int64(i.OutboundRTPPackets))
out += metric("rtsp_sessions_outbound_rtp_packets_reported_lost", ta, int64(i.OutboundRTPPacketsReportedLost)) out += metric("rtsp_sessions_outbound_rtp_packets_reported_lost", ta, int64(i.OutboundRTPPacketsReportedLost))
out += metric("rtsp_sessions_outbound_rtp_packets_discarded", ta, int64(i.OutboundRTPPacketsDiscarded))
out += metric("rtsp_sessions_outbound_rtcp_packets", ta, int64(i.OutboundRTCPPackets)) out += metric("rtsp_sessions_outbound_rtcp_packets", ta, int64(i.OutboundRTCPPackets))
// deprecated // deprecated
out += metric("rtsp_sessions_bytes_received", ta, int64(i.BytesReceived)) out += metric("rtsp_sessions_bytes_received", ta, int64(i.BytesReceived))
@@ -334,6 +349,7 @@ func (m *Metrics) onMetrics(ctx *gin.Context) {
out += metric("rtsp_sessions_outbound_bytes", "", 0) out += metric("rtsp_sessions_outbound_bytes", "", 0)
out += metric("rtsp_sessions_outbound_rtp_packets", "", 0) out += metric("rtsp_sessions_outbound_rtp_packets", "", 0)
out += metric("rtsp_sessions_outbound_rtp_packets_reported_lost", "", 0) out += metric("rtsp_sessions_outbound_rtp_packets_reported_lost", "", 0)
out += metric("rtsp_sessions_outbound_rtp_packets_discarded", "", 0)
out += metric("rtsp_sessions_outbound_rtcp_packets", "", 0) out += metric("rtsp_sessions_outbound_rtcp_packets", "", 0)
// deprecated // deprecated
out += metric("rtsp_sessions_bytes_received", "", 0) out += metric("rtsp_sessions_bytes_received", "", 0)
@@ -401,6 +417,7 @@ func (m *Metrics) onMetrics(ctx *gin.Context) {
out += metric("rtsps_sessions_outbound_bytes", ta, int64(i.OutboundBytes)) out += metric("rtsps_sessions_outbound_bytes", ta, int64(i.OutboundBytes))
out += metric("rtsps_sessions_outbound_rtp_packets", ta, int64(i.OutboundRTPPackets)) out += metric("rtsps_sessions_outbound_rtp_packets", ta, int64(i.OutboundRTPPackets))
out += metric("rtsps_sessions_outbound_rtp_packets_reported_lost", ta, int64(i.OutboundRTPPacketsReportedLost)) out += metric("rtsps_sessions_outbound_rtp_packets_reported_lost", ta, int64(i.OutboundRTPPacketsReportedLost))
out += metric("rtsps_sessions_outbound_rtp_packets_discarded", ta, int64(i.OutboundRTPPacketsDiscarded))
out += metric("rtsps_sessions_outbound_rtcp_packets", ta, int64(i.OutboundRTCPPackets)) out += metric("rtsps_sessions_outbound_rtcp_packets", ta, int64(i.OutboundRTCPPackets))
// deprecated // deprecated
out += metric("rtsps_sessions_bytes_received", ta, int64(i.BytesReceived)) out += metric("rtsps_sessions_bytes_received", ta, int64(i.BytesReceived))
@@ -427,6 +444,7 @@ func (m *Metrics) onMetrics(ctx *gin.Context) {
out += metric("rtsps_sessions_outbound_bytes", "", 0) out += metric("rtsps_sessions_outbound_bytes", "", 0)
out += metric("rtsps_sessions_outbound_rtp_packets", "", 0) out += metric("rtsps_sessions_outbound_rtp_packets", "", 0)
out += metric("rtsps_sessions_outbound_rtp_packets_reported_lost", "", 0) out += metric("rtsps_sessions_outbound_rtp_packets_reported_lost", "", 0)
out += metric("rtsps_sessions_outbound_rtp_packets_discarded", "", 0)
out += metric("rtsps_sessions_outbound_rtcp_packets", "", 0) out += metric("rtsps_sessions_outbound_rtcp_packets", "", 0)
// deprecated // deprecated
out += metric("rtsps_sessions_bytes_received", "", 0) out += metric("rtsps_sessions_bytes_received", "", 0)
@@ -458,12 +476,20 @@ func (m *Metrics) onMetrics(ctx *gin.Context) {
"remoteAddr": i.RemoteAddr, "remoteAddr": i.RemoteAddr,
}) })
out += metric("rtmp_conns", ta, 1) out += metric("rtmp_conns", ta, 1)
out += metric("rtmp_conns_inbound_bytes", ta, int64(i.InboundBytes))
out += metric("rtmp_conns_outbound_bytes", ta, int64(i.OutboundBytes))
out += metric("rtmp_conns_outbound_frames_discarded", ta, int64(i.OutboundFramesDiscarded))
// deprecated
out += metric("rtmp_conns_bytes_received", ta, int64(i.BytesReceived)) out += metric("rtmp_conns_bytes_received", ta, int64(i.BytesReceived))
out += metric("rtmp_conns_bytes_sent", ta, int64(i.BytesSent)) out += metric("rtmp_conns_bytes_sent", ta, int64(i.BytesSent))
} }
} }
} else if rtmpConnFilter == "" { } else if rtmpConnFilter == "" {
out += metric("rtmp_conns", "", 0) out += metric("rtmp_conns", "", 0)
out += metric("rtmp_conns_inbound_bytes", "", 0)
out += metric("rtmp_conns_outbound_bytes", "", 0)
out += metric("rtmp_conns_outbound_frames_discarded", "", 0)
// deprecated
out += metric("rtmp_conns_bytes_received", "", 0) out += metric("rtmp_conns_bytes_received", "", 0)
out += metric("rtmp_conns_bytes_sent", "", 0) out += metric("rtmp_conns_bytes_sent", "", 0)
} }
@@ -484,12 +510,20 @@ func (m *Metrics) onMetrics(ctx *gin.Context) {
"remoteAddr": i.RemoteAddr, "remoteAddr": i.RemoteAddr,
}) })
out += metric("rtmps_conns", ta, 1) out += metric("rtmps_conns", ta, 1)
out += metric("rtmps_conns_inbound_bytes", ta, int64(i.InboundBytes))
out += metric("rtmps_conns_outbound_bytes", ta, int64(i.OutboundBytes))
out += metric("rtmps_conns_outbound_frames_discarded", ta, int64(i.OutboundFramesDiscarded))
// deprecated
out += metric("rtmps_conns_bytes_received", ta, int64(i.BytesReceived)) out += metric("rtmps_conns_bytes_received", ta, int64(i.BytesReceived))
out += metric("rtmps_conns_bytes_sent", ta, int64(i.BytesSent)) out += metric("rtmps_conns_bytes_sent", ta, int64(i.BytesSent))
} }
} }
} else if rtmpsConnFilter == "" { } else if rtmpsConnFilter == "" {
out += metric("rtmps_conns", "", 0) out += metric("rtmps_conns", "", 0)
out += metric("rtmps_conns_inbound_bytes", "", 0)
out += metric("rtmps_conns_outbound_bytes", "", 0)
out += metric("rtmps_conns_outbound_frames_discarded", "", 0)
// deprecated
out += metric("rtmps_conns_bytes_received", "", 0) out += metric("rtmps_conns_bytes_received", "", 0)
out += metric("rtmps_conns_bytes_sent", "", 0) out += metric("rtmps_conns_bytes_sent", "", 0)
} }
@@ -563,6 +597,7 @@ func (m *Metrics) onMetrics(ctx *gin.Context) {
out += metric("srt_conns_packets_received_avg_belated_time", ta, int64(i.PacketsReceivedAvgBelatedTime)) out += metric("srt_conns_packets_received_avg_belated_time", ta, int64(i.PacketsReceivedAvgBelatedTime))
out += metricFloat("srt_conns_packets_send_loss_rate", ta, i.PacketsSendLossRate) out += metricFloat("srt_conns_packets_send_loss_rate", ta, i.PacketsSendLossRate)
out += metricFloat("srt_conns_packets_received_loss_rate", ta, i.PacketsReceivedLossRate) out += metricFloat("srt_conns_packets_received_loss_rate", ta, i.PacketsReceivedLossRate)
out += metric("srt_conns_outbound_frames_discarded", ta, int64(i.OutboundFramesDiscarded))
} }
} }
} else if srtConnFilter == "" { } else if srtConnFilter == "" {
@@ -620,6 +655,7 @@ func (m *Metrics) onMetrics(ctx *gin.Context) {
out += metric("srt_conns_packets_received_avg_belated_time", "", 0) out += metric("srt_conns_packets_received_avg_belated_time", "", 0)
out += metricFloat("srt_conns_packets_send_loss_rate", "", 0) out += metricFloat("srt_conns_packets_send_loss_rate", "", 0)
out += metricFloat("srt_conns_packets_received_loss_rate", "", 0) out += metricFloat("srt_conns_packets_received_loss_rate", "", 0)
out += metric("srt_conns_outbound_frames_discarded", "", 0)
} }
} }
@@ -646,6 +682,7 @@ func (m *Metrics) onMetrics(ctx *gin.Context) {
out += metric("webrtc_sessions_outbound_bytes", ta, int64(i.OutboundBytes)) out += metric("webrtc_sessions_outbound_bytes", ta, int64(i.OutboundBytes))
out += metric("webrtc_sessions_outbound_rtp_packets", ta, int64(i.OutboundRTPPackets)) out += metric("webrtc_sessions_outbound_rtp_packets", ta, int64(i.OutboundRTPPackets))
out += metric("webrtc_sessions_outbound_rtcp_packets", ta, int64(i.OutboundRTCPPackets)) out += metric("webrtc_sessions_outbound_rtcp_packets", ta, int64(i.OutboundRTCPPackets))
out += metric("webrtc_sessions_outbound_frames_discarded", ta, int64(i.OutboundFramesDiscarded))
// deprecated // deprecated
out += metric("webrtc_sessions_bytes_received", ta, int64(i.BytesReceived)) out += metric("webrtc_sessions_bytes_received", ta, int64(i.BytesReceived))
out += metric("webrtc_sessions_bytes_sent", ta, int64(i.BytesSent)) out += metric("webrtc_sessions_bytes_sent", ta, int64(i.BytesSent))
@@ -667,6 +704,7 @@ func (m *Metrics) onMetrics(ctx *gin.Context) {
out += metric("webrtc_sessions_outbound_bytes", "", 0) out += metric("webrtc_sessions_outbound_bytes", "", 0)
out += metric("webrtc_sessions_outbound_rtp_packets", "", 0) out += metric("webrtc_sessions_outbound_rtp_packets", "", 0)
out += metric("webrtc_sessions_outbound_rtcp_packets", "", 0) out += metric("webrtc_sessions_outbound_rtcp_packets", "", 0)
out += metric("webrtc_sessions_outbound_frames_discarded", "", 0)
// deprecated // deprecated
out += metric("webrtc_sessions_bytes_received", "", 0) out += metric("webrtc_sessions_bytes_received", "", 0)
out += metric("webrtc_sessions_bytes_sent", "", 0) out += metric("webrtc_sessions_bytes_sent", "", 0)
+76 -169
View File
@@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"strings"
"testing" "testing"
"time" "time"
@@ -22,6 +23,18 @@ func ptrOf[T any](v T) *T {
return p return p
} }
func requireMetricsLines(t *testing.T, body []byte, lines []string) {
t.Helper()
got := string(body)
for _, line := range lines {
require.Contains(t, got, line+"\n")
}
nonEmptyLines := strings.Count(strings.TrimSpace(got), "\n") + 1
require.GreaterOrEqual(t, nonEmptyLines, len(lines))
}
type dummyPathManager struct{} type dummyPathManager struct{}
func (dummyPathManager) APIPathsList() (*defs.APIPathList, error) { func (dummyPathManager) APIPathsList() (*defs.APIPathList, error) {
@@ -35,11 +48,14 @@ func (dummyPathManager) APIPathsList() (*defs.APIPathList, error) {
Type: defs.APIPathSourceTypeRTSPSession, Type: defs.APIPathSourceTypeRTSPSession,
ID: "123324354", ID: "123324354",
}, },
Ready: true, Ready: true,
ReadyTime: ptrOf(time.Date(2003, 11, 4, 23, 15, 7, 0, time.UTC)), ReadyTime: ptrOf(time.Date(2003, 11, 4, 23, 15, 7, 0, time.UTC)),
Tracks: []defs.APIPathTrackCodec{defs.APIPathTrackCodecH264, defs.APIPathTrackCodecH265}, Tracks: []defs.APIPathTrackCodec{defs.APIPathTrackCodecH264, defs.APIPathTrackCodecH265},
BytesReceived: 123, InboundBytes: 123,
BytesSent: 456, OutboundBytes: 456,
InboundFramesInError: 7,
BytesReceived: 123,
BytesSent: 456,
Readers: []defs.APIPathReader{ Readers: []defs.APIPathReader{
{ {
Type: defs.APIPathReaderTypeRTSPSession, Type: defs.APIPathReaderTypeRTSPSession,
@@ -61,10 +77,12 @@ func (dummyHLSServer) APIMuxersList() (*defs.APIHLSMuxerList, error) {
ItemCount: 1, ItemCount: 1,
PageCount: 1, PageCount: 1,
Items: []defs.APIHLSMuxer{{ Items: []defs.APIHLSMuxer{{
Path: "mypath", Path: "mypath",
Created: time.Date(2003, 11, 4, 23, 15, 7, 0, time.UTC), Created: time.Date(2003, 11, 4, 23, 15, 7, 0, time.UTC),
LastRequest: time.Date(2003, 11, 4, 23, 15, 7, 0, time.UTC), LastRequest: time.Date(2003, 11, 4, 23, 15, 7, 0, time.UTC),
BytesSent: 789, OutboundBytes: 789,
OutboundFramesDiscarded: 12,
BytesSent: 789,
}}, }},
}, nil }, nil
} }
@@ -118,6 +136,7 @@ func (dummyRTSPServer) APISessionsList() (*defs.APIRTSPSessionList, error) {
OutboundBytes: 456, OutboundBytes: 456,
OutboundRTPPackets: 123, OutboundRTPPackets: 123,
OutboundRTPPacketsReportedLost: 321, OutboundRTPPacketsReportedLost: 321,
OutboundRTPPacketsDiscarded: 111,
OutboundRTCPPackets: 789, OutboundRTCPPackets: 789,
BytesReceived: 123, BytesReceived: 123,
BytesSent: 456, BytesSent: 456,
@@ -148,14 +167,17 @@ func (dummyRTMPServer) APIConnsList() (*defs.APIRTMPConnList, error) {
ItemCount: 1, ItemCount: 1,
PageCount: 1, PageCount: 1,
Items: []defs.APIRTMPConn{{ Items: []defs.APIRTMPConn{{
ID: uuid.MustParse("9a07afe4-fc07-4c9b-be6e-6255720c36d0"), ID: uuid.MustParse("9a07afe4-fc07-4c9b-be6e-6255720c36d0"),
Created: time.Date(2003, 11, 4, 23, 15, 7, 0, time.UTC), Created: time.Date(2003, 11, 4, 23, 15, 7, 0, time.UTC),
RemoteAddr: "3.3.3.3:5678", RemoteAddr: "3.3.3.3:5678",
State: defs.APIRTMPConnStateRead, State: defs.APIRTMPConnStateRead,
Path: "mypath", Path: "mypath",
Query: "myquery", Query: "myquery",
BytesReceived: 123, InboundBytes: 123,
BytesSent: 456, OutboundBytes: 456,
OutboundFramesDiscarded: 12,
BytesReceived: 123,
BytesSent: 456,
}}, }},
}, nil }, nil
} }
@@ -192,6 +214,7 @@ func (dummyWebRTCServer) APISessionsList() (*defs.APIWebRTCSessionList, error) {
OutboundBytes: 456, OutboundBytes: 456,
OutboundRTPPackets: 123, OutboundRTPPackets: 123,
OutboundRTCPPackets: 456, OutboundRTCPPackets: 456,
OutboundFramesDiscarded: 12,
BytesReceived: 123, BytesReceived: 123,
BytesSent: 456, BytesSent: 456,
RTPPacketsReceived: 789, RTPPacketsReceived: 789,
@@ -294,158 +317,35 @@ func TestMetrics(t *testing.T) {
byts, err := io.ReadAll(res.Body) byts, err := io.ReadAll(res.Body)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, requireMetricsLines(t, byts, []string{
`paths{name="mypath",state="ready"} 1`+"\n"+ `paths{name="mypath",state="ready"} 1`,
`paths_bytes_received{name="mypath",state="ready"} 123`+"\n"+ `paths_inbound_bytes{name="mypath",state="ready"} 123`,
`paths_bytes_sent{name="mypath",state="ready"} 456`+"\n"+ `paths_outbound_bytes{name="mypath",state="ready"} 456`,
`paths_readers{name="mypath",state="ready"} 1`+"\n"+ `paths_inbound_frames_in_error{name="mypath",state="ready"} 7`,
`hls_muxers{name="mypath"} 1`+"\n"+ `paths_bytes_received{name="mypath",state="ready"} 123`,
`hls_muxers_bytes_sent{name="mypath"} 789`+"\n"+ `paths_bytes_sent{name="mypath",state="ready"} 456`,
`rtsp_conns{id="18294761-f9d1-4ea9-9a35-fe265b62eb41"} 1`+"\n"+ `paths_readers{name="mypath",state="ready"} 1`,
`rtsp_conns_inbound_bytes{id="18294761-f9d1-4ea9-9a35-fe265b62eb41"} 123`+"\n"+ `hls_muxers{name="mypath"} 1`,
`rtsp_conns_outbound_bytes{id="18294761-f9d1-4ea9-9a35-fe265b62eb41"} 456`+"\n"+ `hls_muxers_outbound_bytes{name="mypath"} 789`,
`rtsp_conns_bytes_received{id="18294761-f9d1-4ea9-9a35-fe265b62eb41"} 123`+"\n"+ `hls_muxers_outbound_frames_discarded{name="mypath"} 12`,
`rtsp_conns_bytes_sent{id="18294761-f9d1-4ea9-9a35-fe265b62eb41"} 456`+"\n"+ `rtsp_conns_inbound_bytes{id="18294761-f9d1-4ea9-9a35-fe265b62eb41"} 123`,
`rtsp_sessions{id="124b22ce-9c34-4387-b045-44caf98049f7",`+ `rtsp_sessions_outbound_rtp_packets_discarded{id="124b22ce-9c34-4387-b045-44caf98049f7",` +
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 1`+"\n"+ `path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 111`,
`rtsp_sessions_inbound_bytes{id="124b22ce-9c34-4387-b045-44caf98049f7",`+ `rtsps_sessions_outbound_rtp_packets_discarded{id="124b22ce-9c34-4387-b045-44caf98049f7",` +
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 123`+"\n"+ `path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 111`,
`rtsp_sessions_inbound_rtp_packets{id="124b22ce-9c34-4387-b045-44caf98049f7",`+ `rtmp_conns_inbound_bytes{id="9a07afe4-fc07-4c9b-be6e-6255720c36d0",` +
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 789`+"\n"+ `path="mypath",remoteAddr="3.3.3.3:5678",state="read"} 123`,
`rtsp_sessions_inbound_rtp_packets_lost{id="124b22ce-9c34-4387-b045-44caf98049f7",`+ `rtmp_conns_outbound_bytes{id="9a07afe4-fc07-4c9b-be6e-6255720c36d0",` +
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 456`+"\n"+ `path="mypath",remoteAddr="3.3.3.3:5678",state="read"} 456`,
`rtsp_sessions_inbound_rtp_packets_in_error{id="124b22ce-9c34-4387-b045-44caf98049f7",`+ `rtmp_conns_outbound_frames_discarded{id="9a07afe4-fc07-4c9b-be6e-6255720c36d0",` +
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 789`+"\n"+ `path="mypath",remoteAddr="3.3.3.3:5678",state="read"} 12`,
`rtsp_sessions_inbound_rtp_packets_jitter{id="124b22ce-9c34-4387-b045-44caf98049f7",`+ `rtmp_conns_bytes_received{id="9a07afe4-fc07-4c9b-be6e-6255720c36d0",` +
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 123`+"\n"+ `path="mypath",remoteAddr="3.3.3.3:5678",state="read"} 123`,
`rtsp_sessions_inbound_rtcp_packets{id="124b22ce-9c34-4387-b045-44caf98049f7",`+ `webrtc_sessions_outbound_bytes{id="f47ac10b-58cc-4372-a567-0e02b2c3d479",` +
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 456`+"\n"+ `path="mypath",remoteAddr="127.0.0.1:3455",state="read"} 456`,
`rtsp_sessions_inbound_rtcp_packets_in_error{id="124b22ce-9c34-4387-b045-44caf98049f7",`+ `webrtc_sessions_outbound_frames_discarded{id="f47ac10b-58cc-4372-a567-0e02b2c3d479",` +
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 456`+"\n"+ `path="mypath",remoteAddr="127.0.0.1:3455",state="read"} 12`,
`rtsp_sessions_outbound_bytes{id="124b22ce-9c34-4387-b045-44caf98049f7",`+ })
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 456`+"\n"+
`rtsp_sessions_outbound_rtp_packets{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 123`+"\n"+
`rtsp_sessions_outbound_rtp_packets_reported_lost{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 321`+"\n"+
`rtsp_sessions_outbound_rtcp_packets{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 789`+"\n"+
`rtsp_sessions_bytes_received{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 123`+"\n"+
`rtsp_sessions_bytes_sent{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 456`+"\n"+
`rtsp_sessions_rtp_packets_received{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 789`+"\n"+
`rtsp_sessions_rtp_packets_sent{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 123`+"\n"+
`rtsp_sessions_rtp_packets_lost{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 456`+"\n"+
`rtsp_sessions_rtp_packets_in_error{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 789`+"\n"+
`rtsp_sessions_rtp_packets_jitter{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 123`+"\n"+
`rtsp_sessions_rtcp_packets_received{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 456`+"\n"+
`rtsp_sessions_rtcp_packets_sent{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 789`+"\n"+
`rtsp_sessions_rtcp_packets_in_error{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 456`+"\n"+
`rtsps_conns{id="18294761-f9d1-4ea9-9a35-fe265b62eb41"} 1`+"\n"+
`rtsps_conns_inbound_bytes{id="18294761-f9d1-4ea9-9a35-fe265b62eb41"} 123`+"\n"+
`rtsps_conns_outbound_bytes{id="18294761-f9d1-4ea9-9a35-fe265b62eb41"} 456`+"\n"+
`rtsps_conns_bytes_received{id="18294761-f9d1-4ea9-9a35-fe265b62eb41"} 123`+"\n"+
`rtsps_conns_bytes_sent{id="18294761-f9d1-4ea9-9a35-fe265b62eb41"} 456`+"\n"+
`rtsps_sessions{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 1`+"\n"+
`rtsps_sessions_inbound_bytes{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 123`+"\n"+
`rtsps_sessions_inbound_rtp_packets{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 789`+"\n"+
`rtsps_sessions_inbound_rtp_packets_lost{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 456`+"\n"+
`rtsps_sessions_inbound_rtp_packets_in_error{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 789`+"\n"+
`rtsps_sessions_inbound_rtp_packets_jitter{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 123`+"\n"+
`rtsps_sessions_inbound_rtcp_packets{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 456`+"\n"+
`rtsps_sessions_inbound_rtcp_packets_in_error{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 456`+"\n"+
`rtsps_sessions_outbound_bytes{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 456`+"\n"+
`rtsps_sessions_outbound_rtp_packets{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 123`+"\n"+
`rtsps_sessions_outbound_rtp_packets_reported_lost{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 321`+"\n"+
`rtsps_sessions_outbound_rtcp_packets{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 789`+"\n"+
`rtsps_sessions_bytes_received{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 123`+"\n"+
`rtsps_sessions_bytes_sent{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 456`+"\n"+
`rtsps_sessions_rtp_packets_received{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 789`+"\n"+
`rtsps_sessions_rtp_packets_sent{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 123`+"\n"+
`rtsps_sessions_rtp_packets_lost{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 456`+"\n"+
`rtsps_sessions_rtp_packets_in_error{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 789`+"\n"+
`rtsps_sessions_rtp_packets_jitter{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 123`+"\n"+
`rtsps_sessions_rtcp_packets_received{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 456`+"\n"+
`rtsps_sessions_rtcp_packets_sent{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 789`+"\n"+
`rtsps_sessions_rtcp_packets_in_error{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 456`+"\n"+
`rtmp_conns{id="9a07afe4-fc07-4c9b-be6e-6255720c36d0",`+
`path="mypath",remoteAddr="3.3.3.3:5678",state="read"} 1`+"\n"+
`rtmp_conns_bytes_received{id="9a07afe4-fc07-4c9b-be6e-6255720c36d0",`+
`path="mypath",remoteAddr="3.3.3.3:5678",state="read"} 123`+"\n"+
`rtmp_conns_bytes_sent{id="9a07afe4-fc07-4c9b-be6e-6255720c36d0",`+
`path="mypath",remoteAddr="3.3.3.3:5678",state="read"} 456`+"\n"+
`rtmps_conns{id="9a07afe4-fc07-4c9b-be6e-6255720c36d0",`+
`path="mypath",remoteAddr="3.3.3.3:5678",state="read"} 1`+"\n"+
`rtmps_conns_bytes_received{id="9a07afe4-fc07-4c9b-be6e-6255720c36d0",`+
`path="mypath",remoteAddr="3.3.3.3:5678",state="read"} 123`+"\n"+
`rtmps_conns_bytes_sent{id="9a07afe4-fc07-4c9b-be6e-6255720c36d0",`+
`path="mypath",remoteAddr="3.3.3.3:5678",state="read"} 456`+"\n"+
`webrtc_sessions{id="f47ac10b-58cc-4372-a567-0e02b2c3d479",`+
`path="mypath",remoteAddr="127.0.0.1:3455",state="read"} 1`+"\n"+
`webrtc_sessions_inbound_bytes{id="f47ac10b-58cc-4372-a567-0e02b2c3d479",`+
`path="mypath",remoteAddr="127.0.0.1:3455",state="read"} 123`+"\n"+
`webrtc_sessions_inbound_rtp_packets{id="f47ac10b-58cc-4372-a567-0e02b2c3d479",`+
`path="mypath",remoteAddr="127.0.0.1:3455",state="read"} 789`+"\n"+
`webrtc_sessions_inbound_rtp_packets_lost{id="f47ac10b-58cc-4372-a567-0e02b2c3d479",`+
`path="mypath",remoteAddr="127.0.0.1:3455",state="read"} 456`+"\n"+
`webrtc_sessions_inbound_rtp_packets_jitter{id="f47ac10b-58cc-4372-a567-0e02b2c3d479",`+
`path="mypath",remoteAddr="127.0.0.1:3455",state="read"} 789`+"\n"+
`webrtc_sessions_inbound_rtcp_packets{id="f47ac10b-58cc-4372-a567-0e02b2c3d479",`+
`path="mypath",remoteAddr="127.0.0.1:3455",state="read"} 123`+"\n"+
`webrtc_sessions_outbound_bytes{id="f47ac10b-58cc-4372-a567-0e02b2c3d479",`+
`path="mypath",remoteAddr="127.0.0.1:3455",state="read"} 456`+"\n"+
`webrtc_sessions_outbound_rtp_packets{id="f47ac10b-58cc-4372-a567-0e02b2c3d479",`+
`path="mypath",remoteAddr="127.0.0.1:3455",state="read"} 123`+"\n"+
`webrtc_sessions_outbound_rtcp_packets{id="f47ac10b-58cc-4372-a567-0e02b2c3d479",`+
`path="mypath",remoteAddr="127.0.0.1:3455",state="read"} 456`+"\n"+
`webrtc_sessions_bytes_received{id="f47ac10b-58cc-4372-a567-0e02b2c3d479",`+
`path="mypath",remoteAddr="127.0.0.1:3455",state="read"} 123`+"\n"+
`webrtc_sessions_bytes_sent{id="f47ac10b-58cc-4372-a567-0e02b2c3d479",`+
`path="mypath",remoteAddr="127.0.0.1:3455",state="read"} 456`+"\n"+
`webrtc_sessions_rtp_packets_received{id="f47ac10b-58cc-4372-a567-0e02b2c3d479",`+
`path="mypath",remoteAddr="127.0.0.1:3455",state="read"} 789`+"\n"+
`webrtc_sessions_rtp_packets_sent{id="f47ac10b-58cc-4372-a567-0e02b2c3d479",`+
`path="mypath",remoteAddr="127.0.0.1:3455",state="read"} 123`+"\n"+
`webrtc_sessions_rtp_packets_lost{id="f47ac10b-58cc-4372-a567-0e02b2c3d479",`+
`path="mypath",remoteAddr="127.0.0.1:3455",state="read"} 456`+"\n"+
`webrtc_sessions_rtp_packets_jitter{id="f47ac10b-58cc-4372-a567-0e02b2c3d479",`+
`path="mypath",remoteAddr="127.0.0.1:3455",state="read"} 789`+"\n"+
`webrtc_sessions_rtcp_packets_received{id="f47ac10b-58cc-4372-a567-0e02b2c3d479",`+
`path="mypath",remoteAddr="127.0.0.1:3455",state="read"} 123`+"\n"+
`webrtc_sessions_rtcp_packets_sent{id="f47ac10b-58cc-4372-a567-0e02b2c3d479",`+
`path="mypath",remoteAddr="127.0.0.1:3455",state="read"} 456`+"\n",
string(byts))
require.True(t, checked) require.True(t, checked)
} }
@@ -558,6 +458,9 @@ func TestFilter(t *testing.T) {
case "path": case "path":
require.Equal(t, require.Equal(t,
`paths{name="mypath",state="ready"} 1`+"\n"+ `paths{name="mypath",state="ready"} 1`+"\n"+
`paths_inbound_bytes{name="mypath",state="ready"} 123`+"\n"+
`paths_outbound_bytes{name="mypath",state="ready"} 456`+"\n"+
`paths_inbound_frames_in_error{name="mypath",state="ready"} 7`+"\n"+
`paths_bytes_received{name="mypath",state="ready"} 123`+"\n"+ `paths_bytes_received{name="mypath",state="ready"} 123`+"\n"+
`paths_bytes_sent{name="mypath",state="ready"} 456`+"\n"+ `paths_bytes_sent{name="mypath",state="ready"} 456`+"\n"+
`paths_readers{name="mypath",state="ready"} 1`+"\n", `paths_readers{name="mypath",state="ready"} 1`+"\n",
@@ -566,6 +469,8 @@ func TestFilter(t *testing.T) {
case "hls_muxer": case "hls_muxer":
require.Equal(t, require.Equal(t,
`hls_muxers{name="mypath"} 1`+"\n"+ `hls_muxers{name="mypath"} 1`+"\n"+
`hls_muxers_outbound_bytes{name="mypath"} 789`+"\n"+
`hls_muxers_outbound_frames_discarded{name="mypath"} 12`+"\n"+
`hls_muxers_bytes_sent{name="mypath"} 789`+"\n", `hls_muxers_bytes_sent{name="mypath"} 789`+"\n",
string(byts)) string(byts))
@@ -602,6 +507,8 @@ func TestFilter(t *testing.T) {
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 123`+"\n"+ `path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 123`+"\n"+
`rtsp_sessions_outbound_rtp_packets_reported_lost{id="124b22ce-9c34-4387-b045-44caf98049f7",`+ `rtsp_sessions_outbound_rtp_packets_reported_lost{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 321`+"\n"+ `path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 321`+"\n"+
`rtsp_sessions_outbound_rtp_packets_discarded{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 111`+"\n"+
`rtsp_sessions_outbound_rtcp_packets{id="124b22ce-9c34-4387-b045-44caf98049f7",`+ `rtsp_sessions_outbound_rtcp_packets{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 789`+"\n"+ `path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 789`+"\n"+
`rtsp_sessions_bytes_received{id="124b22ce-9c34-4387-b045-44caf98049f7",`+ `rtsp_sessions_bytes_received{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
+1 -7
View File
@@ -203,13 +203,7 @@ func (s *httpServer) onRequest(ctx *gin.Context) {
return return
} }
mi := mux.getInstance()
if mi == nil {
ctx.Writer.WriteHeader(http.StatusNotFound)
return
}
ctx.Request.URL.Path = fname ctx.Request.URL.Path = fname
mi.handleRequest(ctx) mux.handleRequest(ctx)
} }
} }
+76 -44
View File
@@ -13,6 +13,8 @@ import (
"github.com/bluenviron/mediamtx/internal/defs" "github.com/bluenviron/mediamtx/internal/defs"
"github.com/bluenviron/mediamtx/internal/logger" "github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/protocols/hls" "github.com/bluenviron/mediamtx/internal/protocols/hls"
"github.com/bluenviron/mediamtx/internal/stream"
"github.com/gin-gonic/gin"
) )
const ( const (
@@ -43,8 +45,13 @@ func (w *responseWriterWithCounter) Write(p []byte) (int, error) {
return n, err return n, err
} }
type muxerGetInstanceRes struct {
instance *muxerInstance
cumulatedOutboundFramesDiscarded uint64
}
type muxerGetInstanceReq struct { type muxerGetInstanceReq struct {
res chan *muxerInstance res chan muxerGetInstanceRes
} }
type muxer struct { type muxer struct {
@@ -138,22 +145,7 @@ func (m *muxer) runInner() error {
defer m.path.RemoveReader(defs.PathRemoveReaderReq{Author: m}) defer m.path.RemoveReader(defs.PathRemoveReaderReq{Author: m})
var instanceError chan error mi, err := m.createInstance(res.Stream)
var recreateTimer *time.Timer
mi := &muxerInstance{
variant: m.variant,
segmentCount: m.segmentCount,
segmentDuration: m.segmentDuration,
partDuration: m.partDuration,
segmentMaxSize: m.segmentMaxSize,
directory: m.directory,
pathName: m.pathName,
stream: res.Stream,
bytesSent: m.bytesSent,
parent: m,
}
err = mi.initialize()
if err != nil { if err != nil {
if m.remoteAddr != "" || errors.Is(err, hls.ErrNoSupportedCodecs) { if m.remoteAddr != "" || errors.Is(err, hls.ErrNoSupportedCodecs) {
return err return err
@@ -161,11 +153,6 @@ func (m *muxer) runInner() error {
m.Log(logger.Error, err.Error()) m.Log(logger.Error, err.Error())
mi = nil mi = nil
instanceError = make(chan error)
recreateTimer = time.NewTimer(recreatePause)
} else {
instanceError = mi.errorChan()
recreateTimer = emptyTimer()
} }
defer func() { defer func() {
@@ -174,6 +161,17 @@ func (m *muxer) runInner() error {
} }
}() }()
var instanceError chan error
var recreateTimer *time.Timer
if mi != nil {
instanceError = mi.errorChan()
recreateTimer = emptyTimer()
} else {
instanceError = make(chan error)
recreateTimer = time.NewTimer(recreatePause)
}
var activityCheckTimer *time.Timer var activityCheckTimer *time.Timer
if m.remoteAddr != "" { if m.remoteAddr != "" {
activityCheckTimer = time.NewTimer(closeCheckPeriod) activityCheckTimer = time.NewTimer(closeCheckPeriod)
@@ -181,10 +179,15 @@ func (m *muxer) runInner() error {
activityCheckTimer = emptyTimer() activityCheckTimer = emptyTimer()
} }
cumulatedOutboundFramesDiscarded := uint64(0)
for { for {
select { select {
case req := <-m.chGetInstance: case req := <-m.chGetInstance:
req.res <- mi req.res <- muxerGetInstanceRes{
instance: mi,
cumulatedOutboundFramesDiscarded: cumulatedOutboundFramesDiscarded,
}
case err = <-instanceError: case err = <-instanceError:
if m.remoteAddr != "" { if m.remoteAddr != "" {
@@ -193,24 +196,13 @@ func (m *muxer) runInner() error {
m.Log(logger.Error, err.Error()) m.Log(logger.Error, err.Error())
mi.close() mi.close()
cumulatedOutboundFramesDiscarded += mi.reader.OutboundFramesDiscarded()
mi = nil mi = nil
instanceError = make(chan error) instanceError = make(chan error)
recreateTimer = time.NewTimer(recreatePause) recreateTimer = time.NewTimer(recreatePause)
case <-recreateTimer.C: case <-recreateTimer.C:
mi = &muxerInstance{ mi, err = m.createInstance(res.Stream)
variant: m.variant,
segmentCount: m.segmentCount,
segmentDuration: m.segmentDuration,
partDuration: m.partDuration,
segmentMaxSize: m.segmentMaxSize,
directory: m.directory,
pathName: m.pathName,
stream: res.Stream,
bytesSent: m.bytesSent,
parent: m,
}
err = mi.initialize()
if err != nil { if err != nil {
m.Log(logger.Error, err.Error()) m.Log(logger.Error, err.Error())
mi = nil mi = nil
@@ -232,17 +224,31 @@ func (m *muxer) runInner() error {
} }
} }
func (m *muxer) getInstance() *muxerInstance { func (m *muxer) createInstance(strm *stream.Stream) (*muxerInstance, error) {
atomic.StoreInt64(m.lastRequestTime, time.Now().UnixNano()) mi := &muxerInstance{
variant: m.variant,
segmentCount: m.segmentCount,
segmentDuration: m.segmentDuration,
partDuration: m.partDuration,
segmentMaxSize: m.segmentMaxSize,
directory: m.directory,
pathName: m.pathName,
stream: strm,
parent: m,
}
err := mi.initialize()
return mi, err
}
req := muxerGetInstanceReq{res: make(chan *muxerInstance)} func (m *muxer) getInstance() muxerGetInstanceRes {
req := muxerGetInstanceReq{res: make(chan muxerGetInstanceRes)}
select { select {
case m.chGetInstance <- req: case m.chGetInstance <- req:
return <-req.res return <-req.res
case <-m.ctx.Done(): case <-m.ctx.Done():
return nil return muxerGetInstanceRes{}
} }
} }
@@ -254,11 +260,37 @@ func (m *muxer) APIReaderDescribe() *defs.APIPathReader {
} }
} }
func (m *muxer) handleRequest(ctx *gin.Context) {
atomic.StoreInt64(m.lastRequestTime, time.Now().UnixNano())
res := m.getInstance()
if res.instance == nil {
ctx.Writer.WriteHeader(http.StatusNotFound)
return
}
w := &responseWriterWithCounter{
ResponseWriter: ctx.Writer,
bytesSent: m.bytesSent,
}
res.instance.handleRequest(w, ctx.Request)
}
func (m *muxer) apiItem() *defs.APIHLSMuxer { func (m *muxer) apiItem() *defs.APIHLSMuxer {
res := m.getInstance()
outboundFramesDiscarded := res.cumulatedOutboundFramesDiscarded
if res.instance != nil {
outboundFramesDiscarded += res.instance.reader.OutboundFramesDiscarded()
}
return &defs.APIHLSMuxer{ return &defs.APIHLSMuxer{
Path: m.pathName, Path: m.pathName,
Created: m.created, Created: m.created,
LastRequest: time.Unix(0, atomic.LoadInt64(m.lastRequestTime)), LastRequest: time.Unix(0, atomic.LoadInt64(m.lastRequestTime)),
BytesSent: atomic.LoadUint64(m.bytesSent), OutboundBytes: atomic.LoadUint64(m.bytesSent),
OutboundFramesDiscarded: outboundFramesDiscarded,
BytesSent: atomic.LoadUint64(m.bytesSent),
} }
} }
+3 -9
View File
@@ -1,6 +1,7 @@
package hls package hls
import ( import (
"net/http"
"os" "os"
"path/filepath" "path/filepath"
"time" "time"
@@ -11,7 +12,6 @@ import (
"github.com/bluenviron/mediamtx/internal/logger" "github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/protocols/hls" "github.com/bluenviron/mediamtx/internal/protocols/hls"
"github.com/bluenviron/mediamtx/internal/stream" "github.com/bluenviron/mediamtx/internal/stream"
"github.com/gin-gonic/gin"
) )
type muxerInstance struct { type muxerInstance struct {
@@ -23,7 +23,6 @@ type muxerInstance struct {
directory string directory string
pathName string pathName string
stream *stream.Stream stream *stream.Stream
bytesSent *uint64
parent logger.Writer parent logger.Writer
hmuxer *gohlslib.Muxer hmuxer *gohlslib.Muxer
@@ -89,11 +88,6 @@ func (mi *muxerInstance) errorChan() chan error {
return mi.reader.Error() return mi.reader.Error()
} }
func (mi *muxerInstance) handleRequest(ctx *gin.Context) { func (mi *muxerInstance) handleRequest(w http.ResponseWriter, r *http.Request) {
w := &responseWriterWithCounter{ mi.hmuxer.Handle(w, r)
ResponseWriter: ctx.Writer,
bytesSent: mi.bytesSent,
}
mi.hmuxer.Handle(w, ctx.Request)
} }
+22 -9
View File
@@ -48,6 +48,7 @@ type conn struct {
pathName string pathName string
query string query string
user string user string
reader *stream.Reader
} }
func (c *conn) initialize() { func (c *conn) initialize() {
@@ -210,6 +211,10 @@ func (c *conn) runRead() error {
res.Stream.AddReader(r) res.Stream.AddReader(r)
defer res.Stream.RemoveReader(r) defer res.Stream.RemoveReader(r)
c.mutex.Lock()
c.reader = r
c.mutex.Unlock()
select { select {
case <-c.ctx.Done(): case <-c.ctx.Done():
return fmt.Errorf("terminated") return fmt.Errorf("terminated")
@@ -320,21 +325,29 @@ func (c *conn) apiItem() *defs.APIRTMPConn {
bytesReceived := uint64(0) bytesReceived := uint64(0)
bytesSent := uint64(0) bytesSent := uint64(0)
outboundFramesDiscarded := uint64(0)
if c.rconn != nil { if c.rconn != nil {
bytesReceived = c.rconn.BytesReceived() bytesReceived = c.rconn.BytesReceived()
bytesSent = c.rconn.BytesSent() bytesSent = c.rconn.BytesSent()
} }
if c.reader != nil {
outboundFramesDiscarded = c.reader.OutboundFramesDiscarded()
}
return &defs.APIRTMPConn{ return &defs.APIRTMPConn{
ID: c.uuid, ID: c.uuid,
Created: c.created, Created: c.created,
RemoteAddr: c.remoteAddr().String(), RemoteAddr: c.remoteAddr().String(),
State: c.state, State: c.state,
Path: c.pathName, Path: c.pathName,
Query: c.query, Query: c.query,
User: c.user, User: c.user,
BytesReceived: bytesReceived, InboundBytes: bytesReceived,
BytesSent: bytesSent, OutboundBytes: bytesSent,
BytesReceived: bytesReceived,
BytesSent: bytesSent,
OutboundFramesDiscarded: outboundFramesDiscarded,
} }
} }
+24 -18
View File
@@ -196,15 +196,18 @@ func TestServerPublish(t *testing.T) {
require.Equal(t, &defs.APIRTMPConnList{ require.Equal(t, &defs.APIRTMPConnList{
Items: []defs.APIRTMPConn{ Items: []defs.APIRTMPConn{
{ {
ID: list.Items[0].ID, ID: list.Items[0].ID,
Created: list.Items[0].Created, Created: list.Items[0].Created,
RemoteAddr: list.Items[0].RemoteAddr, RemoteAddr: list.Items[0].RemoteAddr,
State: "publish", State: "publish",
Path: "teststream", Path: "teststream",
Query: "user=myuser&pass=mypass&param=value", Query: "user=myuser&pass=mypass&param=value",
User: "myuser", User: "myuser",
BytesReceived: list.Items[0].BytesReceived, InboundBytes: list.Items[0].InboundBytes,
BytesSent: list.Items[0].BytesSent, OutboundBytes: list.Items[0].OutboundBytes,
OutboundFramesDiscarded: list.Items[0].OutboundFramesDiscarded,
BytesReceived: list.Items[0].BytesReceived,
BytesSent: list.Items[0].BytesSent,
}, },
}, },
}, list) }, list)
@@ -354,15 +357,18 @@ func TestServerRead(t *testing.T) {
require.Equal(t, &defs.APIRTMPConnList{ require.Equal(t, &defs.APIRTMPConnList{
Items: []defs.APIRTMPConn{ Items: []defs.APIRTMPConn{
{ {
ID: list.Items[0].ID, ID: list.Items[0].ID,
Created: list.Items[0].Created, Created: list.Items[0].Created,
RemoteAddr: list.Items[0].RemoteAddr, RemoteAddr: list.Items[0].RemoteAddr,
State: "read", State: "read",
Path: "teststream", Path: "teststream",
Query: "user=myuser&pass=mypass&param=value", Query: "user=myuser&pass=mypass&param=value",
User: "myuser", User: "myuser",
BytesReceived: list.Items[0].BytesReceived, InboundBytes: list.Items[0].InboundBytes,
BytesSent: list.Items[0].BytesSent, OutboundBytes: list.Items[0].OutboundBytes,
OutboundFramesDiscarded: list.Items[0].OutboundFramesDiscarded,
BytesReceived: list.Items[0].BytesReceived,
BytesSent: list.Items[0].BytesSent,
}, },
}, },
}, list) }, list)
+1
View File
@@ -489,6 +489,7 @@ func (s *session) apiItem() *defs.APIRTSPSession {
OutboundBytes: stats.OutboundBytes, OutboundBytes: stats.OutboundBytes,
OutboundRTPPackets: stats.OutboundRTPPackets, OutboundRTPPackets: stats.OutboundRTPPackets,
OutboundRTPPacketsReportedLost: stats.OutboundRTPPacketsReportedLost, OutboundRTPPacketsReportedLost: stats.OutboundRTPPacketsReportedLost,
OutboundRTPPacketsDiscarded: s.outboundRTPPacketsDiscarded.Get(),
OutboundRTCPPackets: stats.OutboundRTCPPackets, OutboundRTCPPackets: stats.OutboundRTCPPackets,
BytesReceived: stats.InboundBytes, BytesReceived: stats.InboundBytes,
BytesSent: stats.OutboundBytes, BytesSent: stats.OutboundBytes,
+9
View File
@@ -66,6 +66,7 @@ type conn struct {
query string query string
user string user string
sconn srt.Conn sconn srt.Conn
reader *stream.Reader
} }
func (c *conn) initialize() { func (c *conn) initialize() {
@@ -335,6 +336,10 @@ func (c *conn) runRead(streamID *streamID) error {
res.Stream.AddReader(r) res.Stream.AddReader(r)
defer res.Stream.RemoveReader(r) defer res.Stream.RemoveReader(r)
c.mutex.Lock()
c.reader = r
c.mutex.Unlock()
select { select {
case <-c.ctx.Done(): case <-c.ctx.Done():
return fmt.Errorf("terminated") return fmt.Errorf("terminated")
@@ -433,5 +438,9 @@ func (c *conn) apiItem() *defs.APISRTConn {
item.PacketsReceivedLossRate = s.Instantaneous.PktRecvLossRate item.PacketsReceivedLossRate = s.Instantaneous.PktRecvLossRate
} }
if c.reader != nil {
item.OutboundFramesDiscarded = c.reader.OutboundFramesDiscarded()
}
return item return item
} }
+1
View File
@@ -216,6 +216,7 @@ func TestServerPublish(t *testing.T) {
BytesSendDrop: list.Items[0].BytesSendDrop, BytesSendDrop: list.Items[0].BytesSendDrop,
BytesReceivedDrop: list.Items[0].BytesReceivedDrop, BytesReceivedDrop: list.Items[0].BytesReceivedDrop,
BytesReceivedUndecrypt: list.Items[0].BytesReceivedUndecrypt, BytesReceivedUndecrypt: list.Items[0].BytesReceivedUndecrypt,
OutboundFramesDiscarded: list.Items[0].OutboundFramesDiscarded,
UsPacketsSendPeriod: list.Items[0].UsPacketsSendPeriod, UsPacketsSendPeriod: list.Items[0].UsPacketsSendPeriod,
PacketsFlowWindow: list.Items[0].PacketsFlowWindow, PacketsFlowWindow: list.Items[0].PacketsFlowWindow,
PacketsFlightSize: list.Items[0].PacketsFlightSize, PacketsFlightSize: list.Items[0].PacketsFlightSize,
+2
View File
@@ -342,6 +342,7 @@ func TestServerPublish(t *testing.T) {
OutboundBytes: list.Items[0].OutboundBytes, OutboundBytes: list.Items[0].OutboundBytes,
OutboundRTPPackets: list.Items[0].OutboundRTPPackets, OutboundRTPPackets: list.Items[0].OutboundRTPPackets,
OutboundRTCPPackets: list.Items[0].OutboundRTCPPackets, OutboundRTCPPackets: list.Items[0].OutboundRTCPPackets,
OutboundFramesDiscarded: list.Items[0].OutboundFramesDiscarded,
BytesReceived: list.Items[0].BytesReceived, BytesReceived: list.Items[0].BytesReceived,
BytesSent: list.Items[0].BytesSent, BytesSent: list.Items[0].BytesSent,
RTPPacketsReceived: list.Items[0].RTPPacketsReceived, RTPPacketsReceived: list.Items[0].RTPPacketsReceived,
@@ -635,6 +636,7 @@ func TestServerRead(t *testing.T) {
OutboundBytes: list.Items[0].OutboundBytes, OutboundBytes: list.Items[0].OutboundBytes,
OutboundRTPPackets: list.Items[0].OutboundRTPPackets, OutboundRTPPackets: list.Items[0].OutboundRTPPackets,
OutboundRTCPPackets: list.Items[0].OutboundRTCPPackets, OutboundRTCPPackets: list.Items[0].OutboundRTCPPackets,
OutboundFramesDiscarded: list.Items[0].OutboundFramesDiscarded,
BytesReceived: list.Items[0].BytesReceived, BytesReceived: list.Items[0].BytesReceived,
BytesSent: list.Items[0].BytesSent, BytesSent: list.Items[0].BytesSent,
RTPPacketsReceived: list.Items[0].RTPPacketsReceived, RTPPacketsReceived: list.Items[0].RTPPacketsReceived,
+11
View File
@@ -63,6 +63,7 @@ type session struct {
uuid uuid.UUID uuid uuid.UUID
secret uuid.UUID secret uuid.UUID
mutex sync.RWMutex mutex sync.RWMutex
reader *stream.Reader
pc *webrtc.PeerConnection pc *webrtc.PeerConnection
user string user string
@@ -387,6 +388,10 @@ func (s *session) runRead() (int, error) {
res.Stream.AddReader(r) res.Stream.AddReader(r)
defer res.Stream.RemoveReader(r) defer res.Stream.RemoveReader(r)
s.mutex.Lock()
s.reader = r
s.mutex.Unlock()
select { select {
case <-pc.Failed(): case <-pc.Failed():
return 0, fmt.Errorf("peer connection closed") return 0, fmt.Errorf("peer connection closed")
@@ -479,6 +484,7 @@ func (s *session) apiItem() *defs.APIWebRTCSession {
rtpPacketsJitter := float64(0) rtpPacketsJitter := float64(0)
rtcpPacketsReceived := uint64(0) rtcpPacketsReceived := uint64(0)
rtcpPacketsSent := uint64(0) rtcpPacketsSent := uint64(0)
outboundFramesDiscarded := uint64(0)
if s.pc != nil { if s.pc != nil {
peerConnectionEstablished = true peerConnectionEstablished = true
@@ -495,6 +501,10 @@ func (s *session) apiItem() *defs.APIWebRTCSession {
rtcpPacketsSent = stats.RTCPPacketsSent rtcpPacketsSent = stats.RTCPPacketsSent
} }
if s.reader != nil {
outboundFramesDiscarded = s.reader.OutboundFramesDiscarded()
}
return &defs.APIWebRTCSession{ return &defs.APIWebRTCSession{
ID: s.uuid, ID: s.uuid,
Created: s.created, Created: s.created,
@@ -519,6 +529,7 @@ func (s *session) apiItem() *defs.APIWebRTCSession {
OutboundBytes: bytesSent, OutboundBytes: bytesSent,
OutboundRTPPackets: rtpPacketsSent, OutboundRTPPackets: rtpPacketsSent,
OutboundRTCPPackets: rtcpPacketsSent, OutboundRTCPPackets: rtcpPacketsSent,
OutboundFramesDiscarded: outboundFramesDiscarded,
BytesReceived: bytesReceived, BytesReceived: bytesReceived,
BytesSent: bytesSent, BytesSent: bytesSent,
RTPPacketsReceived: rtpPacketsReceived, RTPPacketsReceived: rtpPacketsReceived,
+5
View File
@@ -65,6 +65,11 @@ func (r *Reader) Formats() []format.Format {
return out return out
} }
// OutboundFramesDiscarded returns the number of frames discarded because the reader is too slow.
func (r *Reader) OutboundFramesDiscarded() uint64 {
return r.outboundFramesDiscarded.Get()
}
// error returns whenever there's an error. // error returns whenever there's an error.
// It can be called only after stream.AddReader(). // It can be called only after stream.AddReader().
func (r *Reader) Error() chan error { func (r *Reader) Error() chan error {
+5
View File
@@ -483,6 +483,11 @@ func (s *Stream) OutboundBytes() uint64 {
return outboundBytes return outboundBytes
} }
// InboundFramesInError returns the number of frames received with processing errors.
func (s *Stream) InboundFramesInError() uint64 {
return s.inboundFramesInError.Get()
}
// RTSPStream returns the RTSP stream. // RTSPStream returns the RTSP stream.
func (s *Stream) RTSPStream(server *gortsplib.Server) *gortsplib.ServerStream { func (s *Stream) RTSPStream(server *gortsplib.Server) *gortsplib.ServerStream {
s.mutex.Lock() s.mutex.Lock()