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
items:
$ref: '#/components/schemas/PathTrackCodec'
inboundBytes:
type: integer
format: uint64
outboundBytes:
type: integer
format: uint64
inboundFramesInError:
type: integer
format: uint64
bytesReceived:
type: integer
format: uint64
deprecated: true
bytesSent:
type: integer
format: uint64
deprecated: true
readers:
type: array
items:
@@ -1023,9 +1034,16 @@ components:
type: string
lastRequest:
type: string
outboundBytes:
type: integer
format: uint64
outboundFramesDiscarded:
type: integer
format: uint64
bytesSent:
type: integer
format: uint64
deprecated: true
HLSMuxerList:
type: object
@@ -1089,12 +1107,23 @@ components:
type: string
user:
type: string
inboundBytes:
type: integer
format: uint64
outboundBytes:
type: integer
format: uint64
outboundFramesDiscarded:
type: integer
format: uint64
bytesReceived:
type: integer
format: uint64
deprecated: true
bytesSent:
type: integer
format: uint64
deprecated: true
RTMPConnList:
type: object
@@ -1214,6 +1243,9 @@ components:
outboundRTPPacketsReportedLost:
type: integer
format: uint64
outboundRTPPacketsDiscarded:
type: integer
format: uint64
outboundRTCPPackets:
type: integer
format: uint64
@@ -1500,6 +1532,9 @@ components:
type: number
format: double
description: Percentage of retransmitted data vs. received data
outboundFramesDiscarded:
type: integer
format: uint64
SRTConnList:
type: object
@@ -1589,6 +1624,9 @@ components:
outboundRTCPPackets:
type: integer
format: uint64
outboundFramesDiscarded:
type: integer
format: uint64
bytesReceived:
type: integer
format: uint64
+8
View File
@@ -40,12 +40,16 @@ func TestHLSMuxersList(t *testing.T) {
Path: "test1",
Created: now,
LastRequest: now.Add(5 * time.Second),
OutboundBytes: 1234,
OutboundFramesDiscarded: 10,
BytesSent: 1234,
},
"test2": {
Path: "test2",
Created: now.Add(time.Minute),
LastRequest: now.Add(time.Minute + 10*time.Second),
OutboundBytes: 5678,
OutboundFramesDiscarded: 20,
BytesSent: 5678,
},
},
@@ -83,6 +87,8 @@ func TestHLSMuxersGet(t *testing.T) {
Path: "mypath",
Created: now,
LastRequest: now.Add(5 * time.Second),
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)
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)
}
+12
View File
@@ -42,6 +42,9 @@ func TestPathsList(t *testing.T) {
Ready: true,
ReadyTime: &now,
Tracks: []defs.APIPathTrackCodec{defs.APIPathTrackCodecH264, defs.APIPathTrackCodecOpus},
InboundBytes: 1000,
OutboundBytes: 2000,
InboundFramesInError: 3,
BytesReceived: 1000,
BytesSent: 2000,
Readers: []defs.APIPathReader{
@@ -53,6 +56,9 @@ func TestPathsList(t *testing.T) {
ConfName: "test2",
Ready: false,
Tracks: []defs.APIPathTrackCodec{},
InboundBytes: 500,
OutboundBytes: 100,
InboundFramesInError: 1,
BytesReceived: 500,
BytesSent: 100,
Readers: []defs.APIPathReader{},
@@ -95,6 +101,9 @@ func TestPathsGet(t *testing.T) {
Ready: true,
ReadyTime: &now,
Tracks: []defs.APIPathTrackCodec{defs.APIPathTrackCodecH264, defs.APIPathTrackCodecOpus},
InboundBytes: 123456,
OutboundBytes: 789012,
InboundFramesInError: 12,
BytesReceived: 123456,
BytesSent: 789012,
Readers: []defs.APIPathReader{
@@ -131,6 +140,9 @@ func TestPathsGet(t *testing.T) {
require.Equal(t, defs.APIPathSourceTypeRTSPSession, out.Source.Type)
require.Len(t, out.Tracks, 2)
require.Len(t, out.Readers, 2)
require.Equal(t, uint64(123456), out.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(789012), out.BytesSent)
}
+12
View File
@@ -73,6 +73,9 @@ func TestRTMPConnsList(t *testing.T) {
State: defs.APIRTMPConnStatePublish,
Path: "stream1",
Query: "token=abc",
InboundBytes: 1000,
OutboundBytes: 2000,
OutboundFramesDiscarded: 11,
BytesReceived: 1000,
BytesSent: 2000,
},
@@ -83,6 +86,9 @@ func TestRTMPConnsList(t *testing.T) {
State: defs.APIRTMPConnStateRead,
Path: "stream2",
Query: "",
InboundBytes: 500,
OutboundBytes: 1500,
OutboundFramesDiscarded: 22,
BytesReceived: 500,
BytesSent: 1500,
},
@@ -154,6 +160,9 @@ func TestRTMPConnsGet(t *testing.T) {
State: defs.APIRTMPConnStatePublish,
Path: ca.path,
Query: "key=value",
InboundBytes: 999999,
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, defs.APIRTMPConnStatePublish, out.State)
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)
})
}
+4
View File
@@ -62,6 +62,7 @@ func TestSRTConnsList(t *testing.T) {
PacketsReceivedUnique: 1950,
BytesReceived: 100000,
BytesSent: 200000,
OutboundFramesDiscarded: 5,
MsRTT: 10.5,
MbpsSendRate: 5.2,
MbpsReceiveRate: 4.8,
@@ -79,6 +80,7 @@ func TestSRTConnsList(t *testing.T) {
PacketsReceivedUnique: 1470,
BytesReceived: 50000,
BytesSent: 150000,
OutboundFramesDiscarded: 6,
MsRTT: 15.2,
MbpsSendRate: 3.5,
MbpsReceiveRate: 3.2,
@@ -176,6 +178,7 @@ func TestSRTConnsGet(t *testing.T) {
PacketsReceivedAvgBelatedTime: 50,
PacketsSendLossRate: 0.5,
PacketsReceivedLossRate: 0.6,
OutboundFramesDiscarded: 7,
},
},
}
@@ -205,6 +208,7 @@ func TestSRTConnsGet(t *testing.T) {
require.Equal(t, "mystream", out.Path)
require.Equal(t, uint64(999999), out.BytesReceived)
require.Equal(t, uint64(888888), out.BytesSent)
require.Equal(t, uint64(7), out.OutboundFramesDiscarded)
require.Equal(t, 25.5, out.MsRTT)
require.Equal(t, 10.5, out.MbpsSendRate)
require.Equal(t, 9.8, out.MbpsReceiveRate)
+4
View File
@@ -67,6 +67,7 @@ func TestWebRTCSessionsList(t *testing.T) {
OutboundBytes: 2000,
OutboundRTPPackets: 200,
OutboundRTCPPackets: 15,
OutboundFramesDiscarded: 11,
BytesReceived: 1000,
BytesSent: 2000,
RTPPacketsReceived: 100,
@@ -94,6 +95,7 @@ func TestWebRTCSessionsList(t *testing.T) {
OutboundBytes: 1500,
OutboundRTPPackets: 150,
OutboundRTCPPackets: 10,
OutboundFramesDiscarded: 22,
BytesReceived: 500,
BytesSent: 1500,
RTPPacketsReceived: 50,
@@ -154,6 +156,7 @@ func TestWebRTCSessionsGet(t *testing.T) {
OutboundBytes: 888888,
OutboundRTPPackets: 20000,
OutboundRTCPPackets: 200,
OutboundFramesDiscarded: 33,
BytesReceived: 999999,
BytesSent: 888888,
RTPPacketsReceived: 10000,
@@ -196,6 +199,7 @@ func TestWebRTCSessionsGet(t *testing.T) {
require.Equal(t, uint64(888888), out.OutboundBytes)
require.Equal(t, uint64(10000), out.InboundRTPPackets)
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(888888), out.BytesSent)
require.Equal(t, uint64(10000), out.RTPPacketsReceived)
+25
View File
@@ -84,6 +84,9 @@ func TestAPIPathsList(t *testing.T) {
Source pathSource `json:"source"`
Ready bool `json:"ready"`
Tracks []defs.APIPathTrackCodec `json:"tracks"`
InboundBytes uint64 `json:"inboundBytes"`
OutboundBytes uint64 `json:"outboundBytes"`
InboundFramesInError uint64 `json:"inboundFramesInError"`
BytesReceived uint64 `json:"bytesReceived"`
BytesSent uint64 `json:"bytesSent"`
}
@@ -138,6 +141,8 @@ func TestAPIPathsList(t *testing.T) {
},
Ready: true,
Tracks: []defs.APIPathTrackCodec{defs.APIPathTrackCodecH264, defs.APIPathTrackCodecMPEG4Audio},
InboundBytes: 17,
InboundFramesInError: 0,
BytesReceived: 17,
}},
}, out)
@@ -300,6 +305,9 @@ func TestAPIPathsGet(t *testing.T) {
Source pathSource `json:"source"`
Ready bool `json:"Ready"`
Tracks []defs.APIPathTrackCodec `json:"tracks"`
InboundBytes uint64 `json:"inboundBytes"`
OutboundBytes uint64 `json:"outboundBytes"`
InboundFramesInError uint64 `json:"inboundFramesInError"`
BytesReceived uint64 `json:"bytesReceived"`
BytesSent uint64 `json:"bytesSent"`
}
@@ -652,6 +660,7 @@ func TestAPIProtocolListGet(t *testing.T) {
"outboundBytes": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundBytes"],
"outboundRTPPackets": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTPPackets"],
"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"],
"bytesReceived": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesReceived"],
"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"],
"outboundRTPPackets": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTPPackets"],
"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"],
"bytesReceived": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesReceived"],
"bytesSent": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesSent"],
@@ -743,6 +753,9 @@ func TestAPIProtocolListGet(t *testing.T) {
"itemCount": float64(1),
"items": []any{
map[string]any{
"inboundBytes": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["inboundBytes"],
"outboundBytes": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundBytes"],
"outboundFramesDiscarded": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundFramesDiscarded"],
"bytesReceived": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesReceived"],
"bytesSent": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesSent"],
"created": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["created"],
@@ -762,6 +775,9 @@ func TestAPIProtocolListGet(t *testing.T) {
"itemCount": float64(1),
"items": []any{
map[string]any{
"inboundBytes": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["inboundBytes"],
"outboundBytes": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundBytes"],
"outboundFramesDiscarded": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundFramesDiscarded"],
"bytesReceived": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesReceived"],
"bytesSent": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesSent"],
"created": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["created"],
@@ -781,6 +797,8 @@ func TestAPIProtocolListGet(t *testing.T) {
"pageCount": float64(1),
"items": []any{
map[string]any{
"outboundBytes": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundBytes"],
"outboundFramesDiscarded": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundFramesDiscarded"],
"bytesSent": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesSent"],
"created": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["created"],
"lastRequest": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["lastRequest"],
@@ -803,6 +821,7 @@ func TestAPIProtocolListGet(t *testing.T) {
"outboundBytes": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundBytes"],
"outboundRTPPackets": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundRTPPackets"],
"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"],
"bytesSent": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["bytesSent"],
"created": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["created"],
@@ -841,6 +860,7 @@ func TestAPIProtocolListGet(t *testing.T) {
"bytesReceivedLoss": float64(0),
"bytesReceivedRetrans": float64(0),
"bytesReceivedUndecrypt": float64(0),
"outboundFramesDiscarded": out1.(map[string]any)["items"].([]any)[0].(map[string]any)["outboundFramesDiscarded"],
"bytesReceivedUnique": float64(628),
"bytesRetrans": 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)["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)["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)["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"]
@@ -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)["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)["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)["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"]
@@ -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)["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"]
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)
+60
View File
@@ -76,10 +76,15 @@ func TestMetrics(t *testing.T) {
bo := httpPullFile(t, hc, "http://localhost:9998/metrics")
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_sent 0
paths_readers 0
hls_muxers 0
hls_muxers_outbound_bytes 0
hls_muxers_outbound_frames_discarded 0
hls_muxers_bytes_sent 0
rtsp_conns 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_rtp_packets 0
rtsp_sessions_outbound_rtp_packets_reported_lost 0
rtsp_sessions_outbound_rtp_packets_discarded 0
rtsp_sessions_outbound_rtcp_packets 0
rtsp_sessions_bytes_received 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_rtp_packets 0
rtsps_sessions_outbound_rtp_packets_reported_lost 0
rtsps_sessions_outbound_rtp_packets_discarded 0
rtsps_sessions_outbound_rtcp_packets 0
rtsps_sessions_bytes_received 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_in_error 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_sent 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_sent 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_send_loss_rate 0
srt_conns_packets_received_loss_rate 0
srt_conns_outbound_frames_discarded 0
webrtc_sessions 0
webrtc_sessions_inbound_bytes 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_rtp_packets 0
webrtc_sessions_outbound_rtcp_packets 0
webrtc_sessions_outbound_frames_discarded 0
webrtc_sessions_bytes_received 0
webrtc_sessions_bytes_sent 0
webrtc_sessions_rtp_packets_received 0
@@ -395,40 +410,70 @@ webrtc_sessions_rtcp_packets_sent 0
require.Regexp(t,
`^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_sent\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_readers\{name=".*?",state="ready"\} [0-9]+`+"\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_sent\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_readers\{name=".*?",state="ready"\} [0-9]+`+"\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_sent\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_readers\{name=".*?",state="ready"\} [0-9]+`+"\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_sent\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_readers\{name=".*?",state="ready"\} [0-9]+`+"\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_sent\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_readers\{name=".*?",state="ready"\} [0-9]+`+"\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_sent\{name=".*?",state="ready"\} [0-9]+`+"\n"+
`paths_readers\{name=".*?",state="ready"\} [0-9]+`+"\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\{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\{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\{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\{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\{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"+
`rtsp_conns\{id=".*?"\} 1`+"\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_reported_lost\{id=".*?",path=".*?",`+
`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_bytes_received\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} 0`+"\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_reported_lost\{id=".*?",path=".*?",`+
`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_bytes_received\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} 0`+"\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_in_error\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\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_sent\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\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_sent\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\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_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_outbound_frames_discarded\{id=".*?",path=".*?",remoteAddr=".*?",state="publish"\} [0-9]+`+"\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_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_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_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_sent\{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")
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_sent 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)
}(),
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 {
if !pa.isAvailable() {
return 0
+24 -6
View File
@@ -2,7 +2,7 @@
package counterdumper
import (
"sync/atomic"
"sync"
"time"
)
@@ -14,7 +14,9 @@ const (
type Dumper struct {
OnReport func(v uint64)
counter *uint64
mutex sync.Mutex
counter uint64
absCounter uint64
terminate chan struct{}
done chan struct{}
@@ -22,7 +24,6 @@ type Dumper struct {
// Start starts the counter.
func (c *Dumper) Start() {
c.counter = new(uint64)
c.terminate = make(chan struct{})
c.done = make(chan struct{})
@@ -37,12 +38,25 @@ func (c *Dumper) Stop() {
// Increase increases the counter value by 1.
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.
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() {
@@ -57,7 +71,11 @@ func (c *Dumper) run() {
return
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 {
c.OnReport(v)
}
+4 -1
View File
@@ -13,7 +13,10 @@ type APIHLSMuxer struct {
Path string `json:"path"`
Created time.Time `json:"created"`
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.
+6 -2
View File
@@ -73,9 +73,13 @@ type APIPath struct {
OnlineTime *time.Time `json:"onlineTime"`
Source *APIPathSource `json:"source"`
Tracks []APIPathTrackCodec `json:"tracks"`
BytesReceived uint64 `json:"bytesReceived"`
BytesSent uint64 `json:"bytesSent"`
Readers []APIPathReader `json:"readers"`
InboundBytes uint64 `json:"inboundBytes"`
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.
+6 -2
View File
@@ -32,8 +32,12 @@ type APIRTMPConn struct {
Path string `json:"path"`
Query string `json:"query"`
User string `json:"user"`
BytesReceived uint64 `json:"bytesReceived"`
BytesSent uint64 `json:"bytesSent"`
InboundBytes uint64 `json:"inboundBytes"`
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.
+1
View File
@@ -67,6 +67,7 @@ type APIRTSPSession struct {
OutboundBytes uint64 `json:"outboundBytes"`
OutboundRTPPackets uint64 `json:"outboundRTPPackets"`
OutboundRTPPacketsReportedLost uint64 `json:"outboundRTPPacketsReportedLost"`
OutboundRTPPacketsDiscarded uint64 `json:"outboundRTPPacketsDiscarded"`
OutboundRTCPPackets uint64 `json:"outboundRTCPPackets"`
// deprecated
BytesReceived uint64 `json:"bytesReceived" deprecated:"true"`
+2
View File
@@ -153,6 +153,8 @@ type APISRTConn struct {
PacketsSendLossRate float64 `json:"packetsSendLossRate"`
// Percentage of retransmitted data vs. received data
PacketsReceivedLossRate float64 `json:"packetsReceivedLossRate"`
OutboundFramesDiscarded uint64 `json:"outboundFramesDiscarded"`
}
// APISRTConnList is a list of SRT connections.
+1
View File
@@ -42,6 +42,7 @@ type APIWebRTCSession struct {
OutboundBytes uint64 `json:"outboundBytes"`
OutboundRTPPackets uint64 `json:"outboundRTPPackets"`
OutboundRTCPPackets uint64 `json:"outboundRTCPPackets"`
OutboundFramesDiscarded uint64 `json:"outboundFramesDiscarded"`
// deprecated
BytesReceived uint64 `json:"bytesReceived" deprecated:"true"`
BytesSent uint64 `json:"bytesSent" deprecated:"true"`
+9
View File
@@ -16,6 +16,7 @@ type Dumper struct {
mutex sync.Mutex
counter uint64
absCounter uint64
last error
terminate chan struct{}
@@ -41,9 +42,17 @@ func (c *Dumper) Add(err error) {
c.mutex.Lock()
defer c.mutex.Unlock()
c.counter++
c.absCounter++
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() {
defer close(c.done)
+38
View File
@@ -223,6 +223,10 @@ func (m *Metrics) onMetrics(ctx *gin.Context) {
"state": state,
})
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_sent", ta, int64(i.BytesSent))
out += metric("paths_readers", ta, int64(len(i.Readers)))
@@ -230,6 +234,10 @@ func (m *Metrics) onMetrics(ctx *gin.Context) {
}
} else if pathFilter == "" {
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_sent", "", 0)
out += metric("paths_readers", "", 0)
@@ -248,11 +256,17 @@ func (m *Metrics) onMetrics(ctx *gin.Context) {
"name": i.Path,
})
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))
}
}
} else if hlsMuxerFilter == "" {
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)
}
}
@@ -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_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_discarded", ta, int64(i.OutboundRTPPacketsDiscarded))
out += metric("rtsp_sessions_outbound_rtcp_packets", ta, int64(i.OutboundRTCPPackets))
// deprecated
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_rtp_packets", "", 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)
// deprecated
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_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_discarded", ta, int64(i.OutboundRTPPacketsDiscarded))
out += metric("rtsps_sessions_outbound_rtcp_packets", ta, int64(i.OutboundRTCPPackets))
// deprecated
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_rtp_packets", "", 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)
// deprecated
out += metric("rtsps_sessions_bytes_received", "", 0)
@@ -458,12 +476,20 @@ func (m *Metrics) onMetrics(ctx *gin.Context) {
"remoteAddr": i.RemoteAddr,
})
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_sent", ta, int64(i.BytesSent))
}
}
} else if rtmpConnFilter == "" {
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_sent", "", 0)
}
@@ -484,12 +510,20 @@ func (m *Metrics) onMetrics(ctx *gin.Context) {
"remoteAddr": i.RemoteAddr,
})
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_sent", ta, int64(i.BytesSent))
}
}
} else if rtmpsConnFilter == "" {
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_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 += metricFloat("srt_conns_packets_send_loss_rate", ta, i.PacketsSendLossRate)
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 == "" {
@@ -620,6 +655,7 @@ func (m *Metrics) onMetrics(ctx *gin.Context) {
out += metric("srt_conns_packets_received_avg_belated_time", "", 0)
out += metricFloat("srt_conns_packets_send_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_rtp_packets", ta, int64(i.OutboundRTPPackets))
out += metric("webrtc_sessions_outbound_rtcp_packets", ta, int64(i.OutboundRTCPPackets))
out += metric("webrtc_sessions_outbound_frames_discarded", ta, int64(i.OutboundFramesDiscarded))
// deprecated
out += metric("webrtc_sessions_bytes_received", ta, int64(i.BytesReceived))
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_rtp_packets", "", 0)
out += metric("webrtc_sessions_outbound_rtcp_packets", "", 0)
out += metric("webrtc_sessions_outbound_frames_discarded", "", 0)
// deprecated
out += metric("webrtc_sessions_bytes_received", "", 0)
out += metric("webrtc_sessions_bytes_sent", "", 0)
+59 -152
View File
@@ -4,6 +4,7 @@ import (
"fmt"
"io"
"net/http"
"strings"
"testing"
"time"
@@ -22,6 +23,18 @@ func ptrOf[T any](v T) *T {
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{}
func (dummyPathManager) APIPathsList() (*defs.APIPathList, error) {
@@ -38,6 +51,9 @@ func (dummyPathManager) APIPathsList() (*defs.APIPathList, error) {
Ready: true,
ReadyTime: ptrOf(time.Date(2003, 11, 4, 23, 15, 7, 0, time.UTC)),
Tracks: []defs.APIPathTrackCodec{defs.APIPathTrackCodecH264, defs.APIPathTrackCodecH265},
InboundBytes: 123,
OutboundBytes: 456,
InboundFramesInError: 7,
BytesReceived: 123,
BytesSent: 456,
Readers: []defs.APIPathReader{
@@ -64,6 +80,8 @@ func (dummyHLSServer) APIMuxersList() (*defs.APIHLSMuxerList, error) {
Path: "mypath",
Created: time.Date(2003, 11, 4, 23, 15, 7, 0, time.UTC),
LastRequest: time.Date(2003, 11, 4, 23, 15, 7, 0, time.UTC),
OutboundBytes: 789,
OutboundFramesDiscarded: 12,
BytesSent: 789,
}},
}, nil
@@ -118,6 +136,7 @@ func (dummyRTSPServer) APISessionsList() (*defs.APIRTSPSessionList, error) {
OutboundBytes: 456,
OutboundRTPPackets: 123,
OutboundRTPPacketsReportedLost: 321,
OutboundRTPPacketsDiscarded: 111,
OutboundRTCPPackets: 789,
BytesReceived: 123,
BytesSent: 456,
@@ -154,6 +173,9 @@ func (dummyRTMPServer) APIConnsList() (*defs.APIRTMPConnList, error) {
State: defs.APIRTMPConnStateRead,
Path: "mypath",
Query: "myquery",
InboundBytes: 123,
OutboundBytes: 456,
OutboundFramesDiscarded: 12,
BytesReceived: 123,
BytesSent: 456,
}},
@@ -192,6 +214,7 @@ func (dummyWebRTCServer) APISessionsList() (*defs.APIWebRTCSessionList, error) {
OutboundBytes: 456,
OutboundRTPPackets: 123,
OutboundRTCPPackets: 456,
OutboundFramesDiscarded: 12,
BytesReceived: 123,
BytesSent: 456,
RTPPacketsReceived: 789,
@@ -294,158 +317,35 @@ func TestMetrics(t *testing.T) {
byts, err := io.ReadAll(res.Body)
require.NoError(t, err)
require.Equal(t,
`paths{name="mypath",state="ready"} 1`+"\n"+
`paths_bytes_received{name="mypath",state="ready"} 123`+"\n"+
`paths_bytes_sent{name="mypath",state="ready"} 456`+"\n"+
`paths_readers{name="mypath",state="ready"} 1`+"\n"+
`hls_muxers{name="mypath"} 1`+"\n"+
`hls_muxers_bytes_sent{name="mypath"} 789`+"\n"+
`rtsp_conns{id="18294761-f9d1-4ea9-9a35-fe265b62eb41"} 1`+"\n"+
`rtsp_conns_inbound_bytes{id="18294761-f9d1-4ea9-9a35-fe265b62eb41"} 123`+"\n"+
`rtsp_conns_outbound_bytes{id="18294761-f9d1-4ea9-9a35-fe265b62eb41"} 456`+"\n"+
`rtsp_conns_bytes_received{id="18294761-f9d1-4ea9-9a35-fe265b62eb41"} 123`+"\n"+
`rtsp_conns_bytes_sent{id="18294761-f9d1-4ea9-9a35-fe265b62eb41"} 456`+"\n"+
`rtsp_sessions{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 1`+"\n"+
`rtsp_sessions_inbound_bytes{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 123`+"\n"+
`rtsp_sessions_inbound_rtp_packets{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 789`+"\n"+
`rtsp_sessions_inbound_rtp_packets_lost{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 456`+"\n"+
`rtsp_sessions_inbound_rtp_packets_in_error{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 789`+"\n"+
`rtsp_sessions_inbound_rtp_packets_jitter{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 123`+"\n"+
`rtsp_sessions_inbound_rtcp_packets{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 456`+"\n"+
`rtsp_sessions_inbound_rtcp_packets_in_error{id="124b22ce-9c34-4387-b045-44caf98049f7",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 456`+"\n"+
`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))
requireMetricsLines(t, byts, []string{
`paths{name="mypath",state="ready"} 1`,
`paths_inbound_bytes{name="mypath",state="ready"} 123`,
`paths_outbound_bytes{name="mypath",state="ready"} 456`,
`paths_inbound_frames_in_error{name="mypath",state="ready"} 7`,
`paths_bytes_received{name="mypath",state="ready"} 123`,
`paths_bytes_sent{name="mypath",state="ready"} 456`,
`paths_readers{name="mypath",state="ready"} 1`,
`hls_muxers{name="mypath"} 1`,
`hls_muxers_outbound_bytes{name="mypath"} 789`,
`hls_muxers_outbound_frames_discarded{name="mypath"} 12`,
`rtsp_conns_inbound_bytes{id="18294761-f9d1-4ea9-9a35-fe265b62eb41"} 123`,
`rtsp_sessions_outbound_rtp_packets_discarded{id="124b22ce-9c34-4387-b045-44caf98049f7",` +
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 111`,
`rtsps_sessions_outbound_rtp_packets_discarded{id="124b22ce-9c34-4387-b045-44caf98049f7",` +
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 111`,
`rtmp_conns_inbound_bytes{id="9a07afe4-fc07-4c9b-be6e-6255720c36d0",` +
`path="mypath",remoteAddr="3.3.3.3:5678",state="read"} 123`,
`rtmp_conns_outbound_bytes{id="9a07afe4-fc07-4c9b-be6e-6255720c36d0",` +
`path="mypath",remoteAddr="3.3.3.3:5678",state="read"} 456`,
`rtmp_conns_outbound_frames_discarded{id="9a07afe4-fc07-4c9b-be6e-6255720c36d0",` +
`path="mypath",remoteAddr="3.3.3.3:5678",state="read"} 12`,
`rtmp_conns_bytes_received{id="9a07afe4-fc07-4c9b-be6e-6255720c36d0",` +
`path="mypath",remoteAddr="3.3.3.3:5678",state="read"} 123`,
`webrtc_sessions_outbound_bytes{id="f47ac10b-58cc-4372-a567-0e02b2c3d479",` +
`path="mypath",remoteAddr="127.0.0.1:3455",state="read"} 456`,
`webrtc_sessions_outbound_frames_discarded{id="f47ac10b-58cc-4372-a567-0e02b2c3d479",` +
`path="mypath",remoteAddr="127.0.0.1:3455",state="read"} 12`,
})
require.True(t, checked)
}
@@ -558,6 +458,9 @@ func TestFilter(t *testing.T) {
case "path":
require.Equal(t,
`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_sent{name="mypath",state="ready"} 456`+"\n"+
`paths_readers{name="mypath",state="ready"} 1`+"\n",
@@ -566,6 +469,8 @@ func TestFilter(t *testing.T) {
case "hls_muxer":
require.Equal(t,
`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",
string(byts))
@@ -602,6 +507,8 @@ func TestFilter(t *testing.T) {
`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_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",`+
`path="mypath",remoteAddr="124.5.5.5:34542",state="publish"} 789`+"\n"+
`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
}
mi := mux.getInstance()
if mi == nil {
ctx.Writer.WriteHeader(http.StatusNotFound)
return
}
ctx.Request.URL.Path = fname
mi.handleRequest(ctx)
mux.handleRequest(ctx)
}
}
+72 -40
View File
@@ -13,6 +13,8 @@ import (
"github.com/bluenviron/mediamtx/internal/defs"
"github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/protocols/hls"
"github.com/bluenviron/mediamtx/internal/stream"
"github.com/gin-gonic/gin"
)
const (
@@ -43,8 +45,13 @@ func (w *responseWriterWithCounter) Write(p []byte) (int, error) {
return n, err
}
type muxerGetInstanceRes struct {
instance *muxerInstance
cumulatedOutboundFramesDiscarded uint64
}
type muxerGetInstanceReq struct {
res chan *muxerInstance
res chan muxerGetInstanceRes
}
type muxer struct {
@@ -138,22 +145,7 @@ func (m *muxer) runInner() error {
defer m.path.RemoveReader(defs.PathRemoveReaderReq{Author: m})
var instanceError chan error
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()
mi, err := m.createInstance(res.Stream)
if err != nil {
if m.remoteAddr != "" || errors.Is(err, hls.ErrNoSupportedCodecs) {
return err
@@ -161,11 +153,6 @@ func (m *muxer) runInner() error {
m.Log(logger.Error, err.Error())
mi = nil
instanceError = make(chan error)
recreateTimer = time.NewTimer(recreatePause)
} else {
instanceError = mi.errorChan()
recreateTimer = emptyTimer()
}
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
if m.remoteAddr != "" {
activityCheckTimer = time.NewTimer(closeCheckPeriod)
@@ -181,10 +179,15 @@ func (m *muxer) runInner() error {
activityCheckTimer = emptyTimer()
}
cumulatedOutboundFramesDiscarded := uint64(0)
for {
select {
case req := <-m.chGetInstance:
req.res <- mi
req.res <- muxerGetInstanceRes{
instance: mi,
cumulatedOutboundFramesDiscarded: cumulatedOutboundFramesDiscarded,
}
case err = <-instanceError:
if m.remoteAddr != "" {
@@ -193,24 +196,13 @@ func (m *muxer) runInner() error {
m.Log(logger.Error, err.Error())
mi.close()
cumulatedOutboundFramesDiscarded += mi.reader.OutboundFramesDiscarded()
mi = nil
instanceError = make(chan error)
recreateTimer = time.NewTimer(recreatePause)
case <-recreateTimer.C:
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()
mi, err = m.createInstance(res.Stream)
if err != nil {
m.Log(logger.Error, err.Error())
mi = nil
@@ -232,17 +224,31 @@ func (m *muxer) runInner() error {
}
}
func (m *muxer) getInstance() *muxerInstance {
atomic.StoreInt64(m.lastRequestTime, time.Now().UnixNano())
func (m *muxer) createInstance(strm *stream.Stream) (*muxerInstance, error) {
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 {
case m.chGetInstance <- req:
return <-req.res
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 {
res := m.getInstance()
outboundFramesDiscarded := res.cumulatedOutboundFramesDiscarded
if res.instance != nil {
outboundFramesDiscarded += res.instance.reader.OutboundFramesDiscarded()
}
return &defs.APIHLSMuxer{
Path: m.pathName,
Created: m.created,
LastRequest: time.Unix(0, atomic.LoadInt64(m.lastRequestTime)),
OutboundBytes: atomic.LoadUint64(m.bytesSent),
OutboundFramesDiscarded: outboundFramesDiscarded,
BytesSent: atomic.LoadUint64(m.bytesSent),
}
}
+3 -9
View File
@@ -1,6 +1,7 @@
package hls
import (
"net/http"
"os"
"path/filepath"
"time"
@@ -11,7 +12,6 @@ import (
"github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/protocols/hls"
"github.com/bluenviron/mediamtx/internal/stream"
"github.com/gin-gonic/gin"
)
type muxerInstance struct {
@@ -23,7 +23,6 @@ type muxerInstance struct {
directory string
pathName string
stream *stream.Stream
bytesSent *uint64
parent logger.Writer
hmuxer *gohlslib.Muxer
@@ -89,11 +88,6 @@ func (mi *muxerInstance) errorChan() chan error {
return mi.reader.Error()
}
func (mi *muxerInstance) handleRequest(ctx *gin.Context) {
w := &responseWriterWithCounter{
ResponseWriter: ctx.Writer,
bytesSent: mi.bytesSent,
}
mi.hmuxer.Handle(w, ctx.Request)
func (mi *muxerInstance) handleRequest(w http.ResponseWriter, r *http.Request) {
mi.hmuxer.Handle(w, r)
}
+13
View File
@@ -48,6 +48,7 @@ type conn struct {
pathName string
query string
user string
reader *stream.Reader
}
func (c *conn) initialize() {
@@ -210,6 +211,10 @@ func (c *conn) runRead() error {
res.Stream.AddReader(r)
defer res.Stream.RemoveReader(r)
c.mutex.Lock()
c.reader = r
c.mutex.Unlock()
select {
case <-c.ctx.Done():
return fmt.Errorf("terminated")
@@ -320,12 +325,17 @@ func (c *conn) apiItem() *defs.APIRTMPConn {
bytesReceived := uint64(0)
bytesSent := uint64(0)
outboundFramesDiscarded := uint64(0)
if c.rconn != nil {
bytesReceived = c.rconn.BytesReceived()
bytesSent = c.rconn.BytesSent()
}
if c.reader != nil {
outboundFramesDiscarded = c.reader.OutboundFramesDiscarded()
}
return &defs.APIRTMPConn{
ID: c.uuid,
Created: c.created,
@@ -334,7 +344,10 @@ func (c *conn) apiItem() *defs.APIRTMPConn {
Path: c.pathName,
Query: c.query,
User: c.user,
InboundBytes: bytesReceived,
OutboundBytes: bytesSent,
BytesReceived: bytesReceived,
BytesSent: bytesSent,
OutboundFramesDiscarded: outboundFramesDiscarded,
}
}
+6
View File
@@ -203,6 +203,9 @@ func TestServerPublish(t *testing.T) {
Path: "teststream",
Query: "user=myuser&pass=mypass&param=value",
User: "myuser",
InboundBytes: list.Items[0].InboundBytes,
OutboundBytes: list.Items[0].OutboundBytes,
OutboundFramesDiscarded: list.Items[0].OutboundFramesDiscarded,
BytesReceived: list.Items[0].BytesReceived,
BytesSent: list.Items[0].BytesSent,
},
@@ -361,6 +364,9 @@ func TestServerRead(t *testing.T) {
Path: "teststream",
Query: "user=myuser&pass=mypass&param=value",
User: "myuser",
InboundBytes: list.Items[0].InboundBytes,
OutboundBytes: list.Items[0].OutboundBytes,
OutboundFramesDiscarded: list.Items[0].OutboundFramesDiscarded,
BytesReceived: list.Items[0].BytesReceived,
BytesSent: list.Items[0].BytesSent,
},
+1
View File
@@ -489,6 +489,7 @@ func (s *session) apiItem() *defs.APIRTSPSession {
OutboundBytes: stats.OutboundBytes,
OutboundRTPPackets: stats.OutboundRTPPackets,
OutboundRTPPacketsReportedLost: stats.OutboundRTPPacketsReportedLost,
OutboundRTPPacketsDiscarded: s.outboundRTPPacketsDiscarded.Get(),
OutboundRTCPPackets: stats.OutboundRTCPPackets,
BytesReceived: stats.InboundBytes,
BytesSent: stats.OutboundBytes,
+9
View File
@@ -66,6 +66,7 @@ type conn struct {
query string
user string
sconn srt.Conn
reader *stream.Reader
}
func (c *conn) initialize() {
@@ -335,6 +336,10 @@ func (c *conn) runRead(streamID *streamID) error {
res.Stream.AddReader(r)
defer res.Stream.RemoveReader(r)
c.mutex.Lock()
c.reader = r
c.mutex.Unlock()
select {
case <-c.ctx.Done():
return fmt.Errorf("terminated")
@@ -433,5 +438,9 @@ func (c *conn) apiItem() *defs.APISRTConn {
item.PacketsReceivedLossRate = s.Instantaneous.PktRecvLossRate
}
if c.reader != nil {
item.OutboundFramesDiscarded = c.reader.OutboundFramesDiscarded()
}
return item
}
+1
View File
@@ -216,6 +216,7 @@ func TestServerPublish(t *testing.T) {
BytesSendDrop: list.Items[0].BytesSendDrop,
BytesReceivedDrop: list.Items[0].BytesReceivedDrop,
BytesReceivedUndecrypt: list.Items[0].BytesReceivedUndecrypt,
OutboundFramesDiscarded: list.Items[0].OutboundFramesDiscarded,
UsPacketsSendPeriod: list.Items[0].UsPacketsSendPeriod,
PacketsFlowWindow: list.Items[0].PacketsFlowWindow,
PacketsFlightSize: list.Items[0].PacketsFlightSize,
+2
View File
@@ -342,6 +342,7 @@ func TestServerPublish(t *testing.T) {
OutboundBytes: list.Items[0].OutboundBytes,
OutboundRTPPackets: list.Items[0].OutboundRTPPackets,
OutboundRTCPPackets: list.Items[0].OutboundRTCPPackets,
OutboundFramesDiscarded: list.Items[0].OutboundFramesDiscarded,
BytesReceived: list.Items[0].BytesReceived,
BytesSent: list.Items[0].BytesSent,
RTPPacketsReceived: list.Items[0].RTPPacketsReceived,
@@ -635,6 +636,7 @@ func TestServerRead(t *testing.T) {
OutboundBytes: list.Items[0].OutboundBytes,
OutboundRTPPackets: list.Items[0].OutboundRTPPackets,
OutboundRTCPPackets: list.Items[0].OutboundRTCPPackets,
OutboundFramesDiscarded: list.Items[0].OutboundFramesDiscarded,
BytesReceived: list.Items[0].BytesReceived,
BytesSent: list.Items[0].BytesSent,
RTPPacketsReceived: list.Items[0].RTPPacketsReceived,
+11
View File
@@ -63,6 +63,7 @@ type session struct {
uuid uuid.UUID
secret uuid.UUID
mutex sync.RWMutex
reader *stream.Reader
pc *webrtc.PeerConnection
user string
@@ -387,6 +388,10 @@ func (s *session) runRead() (int, error) {
res.Stream.AddReader(r)
defer res.Stream.RemoveReader(r)
s.mutex.Lock()
s.reader = r
s.mutex.Unlock()
select {
case <-pc.Failed():
return 0, fmt.Errorf("peer connection closed")
@@ -479,6 +484,7 @@ func (s *session) apiItem() *defs.APIWebRTCSession {
rtpPacketsJitter := float64(0)
rtcpPacketsReceived := uint64(0)
rtcpPacketsSent := uint64(0)
outboundFramesDiscarded := uint64(0)
if s.pc != nil {
peerConnectionEstablished = true
@@ -495,6 +501,10 @@ func (s *session) apiItem() *defs.APIWebRTCSession {
rtcpPacketsSent = stats.RTCPPacketsSent
}
if s.reader != nil {
outboundFramesDiscarded = s.reader.OutboundFramesDiscarded()
}
return &defs.APIWebRTCSession{
ID: s.uuid,
Created: s.created,
@@ -519,6 +529,7 @@ func (s *session) apiItem() *defs.APIWebRTCSession {
OutboundBytes: bytesSent,
OutboundRTPPackets: rtpPacketsSent,
OutboundRTCPPackets: rtcpPacketsSent,
OutboundFramesDiscarded: outboundFramesDiscarded,
BytesReceived: bytesReceived,
BytesSent: bytesSent,
RTPPacketsReceived: rtpPacketsReceived,
+5
View File
@@ -65,6 +65,11 @@ func (r *Reader) Formats() []format.Format {
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.
// It can be called only after stream.AddReader().
func (r *Reader) Error() chan error {
+5
View File
@@ -483,6 +483,11 @@ func (s *Stream) OutboundBytes() uint64 {
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.
func (s *Stream) RTSPStream(server *gortsplib.Server) *gortsplib.ServerStream {
s.mutex.Lock()