package mtx import ( "context" "fmt" "io" "net/http" "time" "github.com/goccy/go-json" ) // Minimaler Client für MediaMTX Control API v3 // Standard-Ports: API :9997, HLS :8888, RTMP :1935 type Client struct { BaseURL string // z.B. http://127.0.0.1:9997 HTTP *http.Client User string Pass string } type PathsList struct { Items []PathItem `json:"items"` } type PathItem struct { Name string `json:"name"` // publisher kann nil sein, wenn keiner sendet Publisher *SessionRef `json:"publisher"` // readSessions sind aktive Zuschauer ReadSessions []SessionRef `json:"readSessions"` // BytesIn/Out können je nach Version fehlen – deshalb optional halten BytesReceived *int64 `json:"bytesReceived,omitempty"` BytesSent *int64 `json:"bytesSent,omitempty"` } type SessionRef struct { ID string `json:"id"` // Bitrate und weitere Felder variieren je nach Version, wir zeigen defensiv nur das Nötigste an } func (c *Client) do(ctx context.Context, method, path string, out any) error { if c.HTTP == nil { c.HTTP = &http.Client{Timeout: 5 * time.Second} } req, err := http.NewRequestWithContext(ctx, method, c.BaseURL+path, nil) if err != nil { return err } if c.User != "" || c.Pass != "" { req.SetBasicAuth(c.User, c.Pass) } resp, err := c.HTTP.Do(req) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != 200 { b, _ := io.ReadAll(resp.Body) return fmt.Errorf("mtx api %s: %s", resp.Status, string(b)) } return json.NewDecoder(resp.Body).Decode(out) } func (c *Client) Paths(ctx context.Context) (*PathsList, error) { var out PathsList err := c.do(ctx, http.MethodGet, "/v3/paths/list", &out) if err != nil { return nil, err } return &out, nil } func (p PathItem) Live() bool { return p.Publisher != nil } func (p PathItem) Viewers() int { return len(p.ReadSessions) } // HLS-URL Form: http://:8888/ (Playlist wird automatisch geliefert) func HLSURL(publicHost, path string) string { return fmt.Sprintf("%s/%s", publicHost, path) }