mirror of
https://github.com/AlexxIT/go2rtc.git
synced 2025-09-27 04:36:12 +08:00
177 lines
4.0 KiB
Go
177 lines
4.0 KiB
Go
package streams
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
func AppendDOT(dot []byte, stream *Stream) []byte {
|
|
for _, prod := range stream.producers {
|
|
if prod.conn == nil {
|
|
continue
|
|
}
|
|
c, err := marshalConn(prod.conn)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
dot = c.appendDOT(dot, "producer")
|
|
}
|
|
for _, cons := range stream.consumers {
|
|
c, err := marshalConn(cons)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
dot = c.appendDOT(dot, "consumer")
|
|
}
|
|
return dot
|
|
}
|
|
|
|
func marshalConn(v any) (*conn, error) {
|
|
b, err := json.Marshal(v)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var c conn
|
|
if err = json.Unmarshal(b, &c); err != nil {
|
|
return nil, err
|
|
}
|
|
return &c, nil
|
|
}
|
|
|
|
const bytesK = "KMGTP"
|
|
|
|
func humanBytes(i int) string {
|
|
if i < 1000 {
|
|
return fmt.Sprintf("%d B", i)
|
|
}
|
|
|
|
f := float64(i) / 1000
|
|
var n uint8
|
|
for f >= 1000 && n < 5 {
|
|
f /= 1000
|
|
n++
|
|
}
|
|
return fmt.Sprintf("%.2f %cB", f, bytesK[n])
|
|
}
|
|
|
|
type node struct {
|
|
ID uint32 `json:"id"`
|
|
Codec map[string]any `json:"codec"`
|
|
Parent uint32 `json:"parent"`
|
|
Childs []uint32 `json:"childs"`
|
|
Bytes int `json:"bytes"`
|
|
//Packets uint32 `json:"packets"`
|
|
//Drops uint32 `json:"drops"`
|
|
}
|
|
|
|
var codecKeys = []string{"codec_name", "sample_rate", "channels", "profile", "level"}
|
|
|
|
func (n *node) name() string {
|
|
if name, ok := n.Codec["codec_name"].(string); ok {
|
|
return name
|
|
}
|
|
return "unknown"
|
|
}
|
|
|
|
func (n *node) codec() []byte {
|
|
b := make([]byte, 0, 128)
|
|
for _, k := range codecKeys {
|
|
if v := n.Codec[k]; v != nil {
|
|
b = fmt.Appendf(b, "%s=%v\n", k, v)
|
|
}
|
|
}
|
|
if l := len(b); l > 0 {
|
|
return b[:l-1]
|
|
}
|
|
return b
|
|
}
|
|
|
|
func (n *node) appendDOT(dot []byte, group string) []byte {
|
|
dot = fmt.Appendf(dot, "%d [group=%s, label=%q, title=%q];\n", n.ID, group, n.name(), n.codec())
|
|
//for _, sink := range n.Childs {
|
|
// dot = fmt.Appendf(dot, "%d -> %d;\n", n.ID, sink)
|
|
//}
|
|
return dot
|
|
}
|
|
|
|
type conn struct {
|
|
ID uint32 `json:"id"`
|
|
FormatName string `json:"format_name"`
|
|
Protocol string `json:"protocol"`
|
|
RemoteAddr string `json:"remote_addr"`
|
|
Source string `json:"source"`
|
|
URL string `json:"url"`
|
|
UserAgent string `json:"user_agent"`
|
|
Receivers []node `json:"receivers"`
|
|
Senders []node `json:"senders"`
|
|
BytesRecv int `json:"bytes_recv"`
|
|
BytesSend int `json:"bytes_send"`
|
|
}
|
|
|
|
func (c *conn) appendDOT(dot []byte, group string) []byte {
|
|
host := c.host()
|
|
dot = fmt.Appendf(dot, "%s [group=host];\n", host)
|
|
dot = fmt.Appendf(dot, "%d [group=%s, label=%q, title=%q];\n", c.ID, group, c.FormatName, c.label())
|
|
if group == "producer" {
|
|
dot = fmt.Appendf(dot, "%s -> %d [label=%q];\n", host, c.ID, humanBytes(c.BytesRecv))
|
|
} else {
|
|
dot = fmt.Appendf(dot, "%d -> %s [label=%q];\n", c.ID, host, humanBytes(c.BytesSend))
|
|
}
|
|
|
|
for _, recv := range c.Receivers {
|
|
dot = fmt.Appendf(dot, "%d -> %d [label=%q];\n", c.ID, recv.ID, humanBytes(recv.Bytes))
|
|
dot = recv.appendDOT(dot, "node")
|
|
}
|
|
for _, send := range c.Senders {
|
|
dot = fmt.Appendf(dot, "%d -> %d [label=%q];\n", send.Parent, c.ID, humanBytes(send.Bytes))
|
|
//dot = fmt.Appendf(dot, "%d -> %d [label=%q];\n", send.ID, c.ID, humanBytes(send.Bytes))
|
|
//dot = send.appendDOT(dot, "node")
|
|
}
|
|
return dot
|
|
}
|
|
|
|
func (c *conn) host() (s string) {
|
|
if c.Protocol == "pipe" {
|
|
return "127.0.0.1"
|
|
}
|
|
|
|
if s = c.RemoteAddr; s == "" {
|
|
return "unknown"
|
|
}
|
|
|
|
if i := strings.Index(s, "forwarded"); i > 0 {
|
|
s = s[i+10:]
|
|
}
|
|
|
|
if s[0] == '[' {
|
|
if i := strings.Index(s, "]"); i > 0 {
|
|
return s[1:i]
|
|
}
|
|
}
|
|
|
|
if i := strings.IndexAny(s, " ,:"); i > 0 {
|
|
return s[:i]
|
|
}
|
|
return
|
|
}
|
|
|
|
func (c *conn) label() string {
|
|
var sb strings.Builder
|
|
sb.WriteString("format_name=" + c.FormatName)
|
|
if c.Protocol != "" {
|
|
sb.WriteString("\nprotocol=" + c.Protocol)
|
|
}
|
|
if c.Source != "" {
|
|
sb.WriteString("\nsource=" + c.Source)
|
|
}
|
|
if c.URL != "" {
|
|
sb.WriteString("\nurl=" + c.URL)
|
|
}
|
|
if c.UserAgent != "" {
|
|
sb.WriteString("\nuser_agent=" + c.UserAgent)
|
|
}
|
|
// escape quotes https://github.com/AlexxIT/go2rtc/issues/1603
|
|
return strings.ReplaceAll(sb.String(), `"`, `'`)
|
|
}
|