Rewrite stream info API

This commit is contained in:
Alexey Khit
2023-01-15 23:51:20 +03:00
parent bc4e032830
commit dcb457235c
25 changed files with 232 additions and 162 deletions

View File

@@ -3,7 +3,6 @@ package api
import ( import (
"encoding/json" "encoding/json"
"github.com/AlexxIT/go2rtc/cmd/app" "github.com/AlexxIT/go2rtc/cmd/app"
"github.com/AlexxIT/go2rtc/cmd/streams"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"net" "net"
"net/http" "net/http"
@@ -41,7 +40,6 @@ func Init() {
HandleFunc("api", apiHandler) HandleFunc("api", apiHandler)
HandleFunc("api/config", configHandler) HandleFunc("api/config", configHandler)
HandleFunc("api/exit", exitHandler) HandleFunc("api/exit", exitHandler)
HandleFunc("api/streams", streamsHandler)
HandleFunc("api/ws", apiWS) HandleFunc("api/ws", apiWS)
// ensure we can listen without errors // ensure we can listen without errors
@@ -124,32 +122,3 @@ func exitHandler(w http.ResponseWriter, r *http.Request) {
code, _ := strconv.Atoi(s) code, _ := strconv.Atoi(s)
os.Exit(code) os.Exit(code)
} }
func streamsHandler(w http.ResponseWriter, r *http.Request) {
src := r.URL.Query().Get("src")
name := r.URL.Query().Get("name")
if name == "" {
name = src
}
switch r.Method {
case "PUT":
streams.New(name, src)
return
case "DELETE":
streams.Delete(src)
return
}
var v interface{}
if src != "" {
v = streams.Get(src)
} else {
v = streams.All()
}
e := json.NewEncoder(w)
e.SetIndent("", " ")
_ = e.Encode(v)
}

View File

@@ -27,7 +27,10 @@ func handlerKeyframe(w http.ResponseWriter, r *http.Request) {
exit := make(chan []byte) exit := make(chan []byte)
cons := &mjpeg.Consumer{} cons := &mjpeg.Consumer{
RemoteAddr: r.RemoteAddr,
UserAgent: r.UserAgent(),
}
cons.Listen(func(msg interface{}) { cons.Listen(func(msg interface{}) {
switch msg := msg.(type) { switch msg := msg.(type) {
case []byte: case []byte:
@@ -68,7 +71,10 @@ func handlerStream(w http.ResponseWriter, r *http.Request) {
flusher := w.(http.Flusher) flusher := w.(http.Flusher)
cons := &mjpeg.Consumer{} cons := &mjpeg.Consumer{
RemoteAddr: r.RemoteAddr,
UserAgent: r.UserAgent(),
}
cons.Listen(func(msg interface{}) { cons.Listen(func(msg interface{}) {
switch msg := msg.(type) { switch msg := msg.(type) {
case []byte: case []byte:
@@ -109,7 +115,10 @@ func handlerWS(tr *api.Transport, _ *api.Message) error {
return errors.New(api.StreamNotFound) return errors.New(api.StreamNotFound)
} }
cons := &mjpeg.Consumer{} cons := &mjpeg.Consumer{
RemoteAddr: tr.Request.RemoteAddr,
UserAgent: tr.Request.UserAgent(),
}
cons.Listen(func(msg interface{}) { cons.Listen(func(msg interface{}) {
if data, ok := msg.([]byte); ok { if data, ok := msg.([]byte); ok {
tr.Write(data) tr.Write(data)

View File

@@ -80,7 +80,10 @@ func handlerMP4(w http.ResponseWriter, r *http.Request) {
exit := make(chan error) exit := make(chan error)
cons := &mp4.Consumer{} cons := &mp4.Consumer{
RemoteAddr: r.RemoteAddr,
UserAgent: r.UserAgent(),
}
cons.Listen(func(msg interface{}) { cons.Listen(func(msg interface{}) {
if data, ok := msg.([]byte); ok { if data, ok := msg.([]byte); ok {
if _, err := w.Write(data); err != nil && exit != nil { if _, err := w.Write(data); err != nil && exit != nil {

View File

@@ -18,7 +18,10 @@ func handlerWSMSE(tr *api.Transport, msg *api.Message) error {
return errors.New(api.StreamNotFound) return errors.New(api.StreamNotFound)
} }
cons := &mp4.Consumer{} cons := &mp4.Consumer{
RemoteAddr: tr.Request.RemoteAddr,
UserAgent: tr.Request.UserAgent(),
}
cons.UserAgent = tr.Request.UserAgent() cons.UserAgent = tr.Request.UserAgent()
cons.RemoteAddr = tr.Request.RemoteAddr cons.RemoteAddr = tr.Request.RemoteAddr

15
cmd/streams/consumer.go Normal file
View File

@@ -0,0 +1,15 @@
package streams
import (
"encoding/json"
"github.com/AlexxIT/go2rtc/pkg/streamer"
)
type Consumer struct {
element streamer.Consumer
tracks []*streamer.Track
}
func (c *Consumer) MarshalJSON() ([]byte, error) {
return json.Marshal(c.element)
}

View File

@@ -1,6 +1,7 @@
package streams package streams
import ( import (
"encoding/json"
"github.com/AlexxIT/go2rtc/pkg/streamer" "github.com/AlexxIT/go2rtc/pkg/streamer"
"strings" "strings"
"sync" "sync"
@@ -91,6 +92,15 @@ func (p *Producer) GetTrack(media *streamer.Media, codec *streamer.Codec) *strea
return track return track
} }
func (p *Producer) MarshalJSON() ([]byte, error) {
if p.element != nil {
return json.Marshal(p.element)
}
info := streamer.Info{URL: p.url}
return json.Marshal(info)
}
// internals // internals
func (p *Producer) start() { func (p *Producer) start() {

View File

@@ -10,11 +10,6 @@ import (
"sync/atomic" "sync/atomic"
) )
type Consumer struct {
element streamer.Consumer
tracks []*streamer.Track
}
type Stream struct { type Stream struct {
producers []*Producer producers []*Producer
consumers []*Consumer consumers []*Consumer
@@ -199,24 +194,19 @@ producers:
func (s *Stream) MarshalJSON() ([]byte, error) { func (s *Stream) MarshalJSON() ([]byte, error) {
if !s.mu.TryLock() { if !s.mu.TryLock() {
log.Warn().Msgf("[streams] json locked") log.Warn().Msgf("[streams] json locked")
return []byte(`null`), nil return json.Marshal(nil)
} }
var v []interface{} var info struct {
for _, prod := range s.producers { Producers []*Producer `json:"producers"`
if prod.element != nil { Consumers []*Consumer `json:"consumers"`
v = append(v, prod.element)
}
}
for _, cons := range s.consumers {
// cons.element always not nil
v = append(v, cons.element)
} }
info.Producers = s.producers
info.Consumers = s.consumers
s.mu.Unlock() s.mu.Unlock()
if len(v) == 0 {
v = nil return json.Marshal(info)
}
return json.Marshal(v)
} }
func (s *Stream) removeConsumer(i int) { func (s *Stream) removeConsumer(i int) {

View File

@@ -1,9 +1,12 @@
package streams package streams
import ( import (
"encoding/json"
"github.com/AlexxIT/go2rtc/cmd/api"
"github.com/AlexxIT/go2rtc/cmd/app" "github.com/AlexxIT/go2rtc/cmd/app"
"github.com/AlexxIT/go2rtc/cmd/app/store" "github.com/AlexxIT/go2rtc/cmd/app/store"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"net/http"
) )
func Init() { func Init() {
@@ -22,6 +25,8 @@ func Init() {
for name, item := range store.GetDict("streams") { for name, item := range store.GetDict("streams") {
streams[name] = NewStream(item) streams[name] = NewStream(item)
} }
api.HandleFunc("api/streams", streamsHandler)
} }
func Get(name string) *Stream { func Get(name string) *Stream {
@@ -48,19 +53,29 @@ func GetOrNew(src string) *Stream {
return New(src, src) return New(src, src)
} }
func Delete(name string) { func streamsHandler(w http.ResponseWriter, r *http.Request) {
delete(streams, name) src := r.URL.Query().Get("src")
switch r.Method {
case "PUT":
name := r.URL.Query().Get("name")
if name == "" {
name = src
}
New(name, src)
return
case "DELETE":
delete(streams, src)
return
} }
func All() map[string]interface{} { if src != "" {
all := map[string]interface{}{} e := json.NewEncoder(w)
for name, stream := range streams { e.SetIndent("", " ")
all[name] = stream _ = e.Encode(streams[src])
//if stream.Active() { } else {
// all[name] = stream _ = json.NewEncoder(w).Encode(streams)
//}
} }
return all
} }
var log zerolog.Logger var log zerolog.Logger

View File

@@ -1,6 +1,7 @@
package homekit package homekit
import ( import (
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/AlexxIT/go2rtc/pkg/hap" "github.com/AlexxIT/go2rtc/pkg/hap"
@@ -11,6 +12,7 @@ import (
"github.com/brutella/hap/rtp" "github.com/brutella/hap/rtp"
"net" "net"
"net/url" "net/url"
"sync/atomic"
) )
type Client struct { type Client struct {
@@ -263,3 +265,19 @@ func (c *Client) getMedias() []*streamer.Media {
return medias return medias
} }
func (c *Client) MarshalJSON() ([]byte, error) {
var recv uint32
for _, session := range c.sessions {
recv += atomic.LoadUint32(&session.Recv)
}
info := &streamer.Info{
Type: "HomeKit source",
URL: c.conn.URL(),
Medias: c.medias,
Tracks: c.tracks,
Recv: recv,
}
return json.Marshal(info)
}

View File

@@ -15,6 +15,7 @@ import (
"net/http" "net/http"
"strings" "strings"
"sync" "sync"
"sync/atomic"
"time" "time"
) )
@@ -41,6 +42,8 @@ type Client struct {
buffer chan []byte buffer chan []byte
state State state State
mu sync.Mutex mu sync.Mutex
recv uint32
} }
func NewClient(id string) *Client { func NewClient(id string) *Client {
@@ -109,6 +112,7 @@ func (c *Client) Handle() error {
c.mu.Lock() c.mu.Lock()
if c.state == StateHandle { if c.state == StateHandle {
c.buffer <- data c.buffer <- data
atomic.AddUint32(&c.recv, uint32(len(data)))
} }
c.mu.Unlock() c.mu.Unlock()
} }
@@ -140,6 +144,7 @@ func (c *Client) Handle() error {
c.mu.Lock() c.mu.Lock()
if c.state == StateHandle { if c.state == StateHandle {
c.buffer <- data c.buffer <- data
atomic.AddUint32(&c.recv, uint32(len(data)))
} }
c.mu.Unlock() c.mu.Unlock()
} }

View File

@@ -1,8 +1,10 @@
package ivideon package ivideon
import ( import (
"encoding/json"
"fmt" "fmt"
"github.com/AlexxIT/go2rtc/pkg/streamer" "github.com/AlexxIT/go2rtc/pkg/streamer"
"sync/atomic"
) )
func (c *Client) GetMedias() []*streamer.Media { func (c *Client) GetMedias() []*streamer.Media {
@@ -29,3 +31,19 @@ func (c *Client) Start() error {
func (c *Client) Stop() error { func (c *Client) Stop() error {
return c.Close() return c.Close()
} }
func (c *Client) MarshalJSON() ([]byte, error) {
var tracks []*streamer.Track
for _, track := range c.tracks {
tracks = append(tracks, track)
}
info := &streamer.Info{
Type: "Ivideon source",
URL: c.ID,
Medias: c.medias,
Tracks: tracks,
Recv: atomic.LoadUint32(&c.recv),
}
return json.Marshal(info)
}

View File

@@ -2,6 +2,7 @@ package mjpeg
import ( import (
"bufio" "bufio"
"encoding/json"
"errors" "errors"
"github.com/AlexxIT/go2rtc/pkg/streamer" "github.com/AlexxIT/go2rtc/pkg/streamer"
"github.com/AlexxIT/go2rtc/pkg/tcp" "github.com/AlexxIT/go2rtc/pkg/tcp"
@@ -11,6 +12,7 @@ import (
"net/textproto" "net/textproto"
"strconv" "strconv"
"strings" "strings"
"sync/atomic"
"time" "time"
) )
@@ -24,6 +26,7 @@ type Client struct {
res *http.Response res *http.Response
track *streamer.Track track *streamer.Track
recv uint32
} }
func NewClient(res *http.Response) *Client { func NewClient(res *http.Response) *Client {
@@ -70,6 +73,17 @@ func (c *Client) Stop() error {
return nil return nil
} }
func (c *Client) MarshalJSON() ([]byte, error) {
info := &streamer.Info{
Type: "MJPEG source",
URL: c.res.Request.URL.String(),
RemoteAddr: c.RemoteAddr,
UserAgent: c.UserAgent,
Recv: atomic.LoadUint32(&c.recv),
}
return json.Marshal(info)
}
func (c *Client) startJPEG() error { func (c *Client) startJPEG() error {
buf, err := io.ReadAll(c.res.Body) buf, err := io.ReadAll(c.res.Body)
if err != nil { if err != nil {
@@ -79,6 +93,8 @@ func (c *Client) startJPEG() error {
packet := &rtp.Packet{Header: rtp.Header{Timestamp: now()}, Payload: buf} packet := &rtp.Packet{Header: rtp.Header{Timestamp: now()}, Payload: buf}
_ = c.track.WriteRTP(packet) _ = c.track.WriteRTP(packet)
atomic.AddUint32(&c.recv, uint32(len(buf)))
req := c.res.Request req := c.res.Request
for !c.closed { for !c.closed {
@@ -98,6 +114,8 @@ func (c *Client) startJPEG() error {
packet = &rtp.Packet{Header: rtp.Header{Timestamp: now()}, Payload: buf} packet = &rtp.Packet{Header: rtp.Header{Timestamp: now()}, Payload: buf}
_ = c.track.WriteRTP(packet) _ = c.track.WriteRTP(packet)
atomic.AddUint32(&c.recv, uint32(len(buf)))
} }
return nil return nil
@@ -141,6 +159,8 @@ func (c *Client) startMJPEG(boundary string) error {
packet := &rtp.Packet{Header: rtp.Header{Timestamp: now()}, Payload: buf} packet := &rtp.Packet{Header: rtp.Header{Timestamp: now()}, Payload: buf}
_ = c.track.WriteRTP(packet) _ = c.track.WriteRTP(packet)
atomic.AddUint32(&c.recv, uint32(len(buf)))
if _, err = r.Discard(2); err != nil { if _, err = r.Discard(2); err != nil {
return err return err
} }

View File

@@ -1,8 +1,10 @@
package mjpeg package mjpeg
import ( import (
"encoding/json"
"github.com/AlexxIT/go2rtc/pkg/streamer" "github.com/AlexxIT/go2rtc/pkg/streamer"
"github.com/pion/rtp" "github.com/pion/rtp"
"sync/atomic"
) )
type Consumer struct { type Consumer struct {
@@ -14,7 +16,7 @@ type Consumer struct {
codecs []*streamer.Codec codecs []*streamer.Codec
start bool start bool
send int send uint32
} }
func (c *Consumer) GetMedias() []*streamer.Media { func (c *Consumer) GetMedias() []*streamer.Media {
@@ -28,6 +30,7 @@ func (c *Consumer) GetMedias() []*streamer.Media {
func (c *Consumer) AddTrack(media *streamer.Media, track *streamer.Track) *streamer.Track { func (c *Consumer) AddTrack(media *streamer.Media, track *streamer.Track) *streamer.Track {
push := func(packet *rtp.Packet) error { push := func(packet *rtp.Packet) error {
c.Fire(packet.Payload) c.Fire(packet.Payload)
atomic.AddUint32(&c.send, uint32(len(packet.Payload)))
return nil return nil
} }
@@ -38,3 +41,13 @@ func (c *Consumer) AddTrack(media *streamer.Media, track *streamer.Track) *strea
return track.Bind(push) return track.Bind(push)
} }
func (c *Consumer) MarshalJSON() ([]byte, error) {
info := &streamer.Info{
Type: "MJPEG client",
RemoteAddr: c.RemoteAddr,
UserAgent: c.UserAgent,
Send: atomic.LoadUint32(&c.send),
}
return json.Marshal(info)
}

View File

@@ -7,6 +7,7 @@ import (
"github.com/AlexxIT/go2rtc/pkg/h265" "github.com/AlexxIT/go2rtc/pkg/h265"
"github.com/AlexxIT/go2rtc/pkg/streamer" "github.com/AlexxIT/go2rtc/pkg/streamer"
"github.com/pion/rtp" "github.com/pion/rtp"
"sync/atomic"
) )
type Consumer struct { type Consumer struct {
@@ -20,7 +21,7 @@ type Consumer struct {
codecs []*streamer.Codec codecs []*streamer.Codec
wait byte wait byte
send int send uint32
} }
const ( const (
@@ -76,7 +77,7 @@ func (c *Consumer) AddTrack(media *streamer.Media, track *streamer.Track) *strea
} }
buf := c.muxer.Marshal(trackID, packet) buf := c.muxer.Marshal(trackID, packet)
c.send += len(buf) atomic.AddUint32(&c.send, uint32(len(buf)))
c.Fire(buf) c.Fire(buf)
return nil return nil
@@ -108,7 +109,7 @@ func (c *Consumer) AddTrack(media *streamer.Media, track *streamer.Track) *strea
} }
buf := c.muxer.Marshal(trackID, packet) buf := c.muxer.Marshal(trackID, packet)
c.send += len(buf) atomic.AddUint32(&c.send, uint32(len(buf)))
c.Fire(buf) c.Fire(buf)
return nil return nil
@@ -128,7 +129,7 @@ func (c *Consumer) AddTrack(media *streamer.Media, track *streamer.Track) *strea
} }
buf := c.muxer.Marshal(trackID, packet) buf := c.muxer.Marshal(trackID, packet)
c.send += len(buf) atomic.AddUint32(&c.send, uint32(len(buf)))
c.Fire(buf) c.Fire(buf)
return nil return nil
@@ -163,12 +164,11 @@ func (c *Consumer) Start() {
// //
func (c *Consumer) MarshalJSON() ([]byte, error) { func (c *Consumer) MarshalJSON() ([]byte, error) {
v := map[string]interface{}{ info := &streamer.Info{
"type": "MP4 server consumer", Type: "MP4 client",
"send": c.send, RemoteAddr: c.RemoteAddr,
"remote_addr": c.RemoteAddr, UserAgent: c.UserAgent,
"user_agent": c.UserAgent, Send: atomic.LoadUint32(&c.send),
} }
return json.Marshal(info)
return json.Marshal(v)
} }

View File

@@ -1,7 +1,6 @@
package mp4f package mp4f
import ( import (
"encoding/json"
"github.com/AlexxIT/go2rtc/pkg/h264" "github.com/AlexxIT/go2rtc/pkg/h264"
"github.com/AlexxIT/go2rtc/pkg/streamer" "github.com/AlexxIT/go2rtc/pkg/streamer"
"github.com/deepch/vdk/av" "github.com/deepch/vdk/av"
@@ -149,16 +148,3 @@ func (c *Consumer) Init() ([]byte, error) {
func (c *Consumer) Start() { func (c *Consumer) Start() {
c.start = true c.start = true
} }
//
func (c *Consumer) MarshalJSON() ([]byte, error) {
v := map[string]interface{}{
"type": "MSE server consumer",
"send": c.send,
"remote_addr": c.RemoteAddr,
"user_agent": c.UserAgent,
}
return json.Marshal(v)
}

View File

@@ -12,6 +12,7 @@ import (
"github.com/deepch/vdk/format/rtmp" "github.com/deepch/vdk/format/rtmp"
"github.com/pion/rtp" "github.com/pion/rtp"
"net/http" "net/http"
"sync/atomic"
"time" "time"
) )
@@ -33,7 +34,7 @@ type Client struct {
conn Conn conn Conn
closed bool closed bool
receive int recv uint32
} }
func NewClient(uri string) *Client { func NewClient(uri string) *Client {
@@ -138,7 +139,7 @@ func (c *Client) Handle() (err error) {
return return
} }
c.receive += len(pkt.Data) atomic.AddUint32(&c.recv, uint32(len(pkt.Data)))
track := c.tracks[int(pkt.Idx)] track := c.tracks[int(pkt.Idx)]

View File

@@ -4,7 +4,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/AlexxIT/go2rtc/pkg/streamer" "github.com/AlexxIT/go2rtc/pkg/streamer"
"strconv" "sync/atomic"
) )
func (c *Client) GetMedias() []*streamer.Media { func (c *Client) GetMedias() []*streamer.Media {
@@ -29,19 +29,12 @@ func (c *Client) Stop() error {
} }
func (c *Client) MarshalJSON() ([]byte, error) { func (c *Client) MarshalJSON() ([]byte, error) {
v := map[string]interface{}{ info := &streamer.Info{
streamer.JSONReceive: c.receive, Type: "RTMP source",
streamer.JSONType: "RTMP client producer", URL: c.URI,
//streamer.JSONRemoteAddr: c.conn.NetConn().RemoteAddr().String(), Medias: c.medias,
"url": c.URI, Tracks: c.tracks,
Recv: atomic.LoadUint32(&c.recv),
} }
for i, media := range c.medias { return json.Marshal(info)
k := "media:" + strconv.Itoa(i)
v[k] = media.String()
}
for i, track := range c.tracks {
k := "track:" + strconv.Itoa(i)
v[k] = track.String()
}
return json.Marshal(v)
} }

View File

@@ -4,7 +4,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/AlexxIT/go2rtc/pkg/streamer" "github.com/AlexxIT/go2rtc/pkg/streamer"
"strconv"
) )
// Element Producer // Element Producer
@@ -88,40 +87,30 @@ func (c *Conn) AddTrack(media *streamer.Media, track *streamer.Track) *streamer.
// //
func (c *Conn) MarshalJSON() ([]byte, error) { func (c *Conn) MarshalJSON() ([]byte, error) {
v := map[string]interface{}{ info := &streamer.Info{
streamer.JSONReceive: c.receive, UserAgent: c.UserAgent,
streamer.JSONSend: c.send, Medias: c.Medias,
Tracks: c.tracks,
Recv: uint32(c.receive),
Send: uint32(c.send),
} }
switch c.mode { switch c.mode {
case ModeUnknown: case ModeUnknown:
v[streamer.JSONType] = "RTSP unknown" info.Type = "RTSP unknown"
case ModeClientProducer: case ModeClientProducer, ModeServerProducer:
v[streamer.JSONType] = "RTSP client producer" info.Type = "RTSP source"
case ModeServerProducer:
v[streamer.JSONType] = "RTSP server producer"
case ModeServerConsumer: case ModeServerConsumer:
v[streamer.JSONType] = "RTSP server consumer" info.Type = "RTSP client"
} }
//if c.URI != "" {
// v["uri"] = c.URI
//}
if c.URL != nil { if c.URL != nil {
v["url"] = c.URL.String() info.URL = c.URL.String()
} }
if c.conn != nil { if c.conn != nil {
v[streamer.JSONRemoteAddr] = c.conn.RemoteAddr().String() info.RemoteAddr = c.conn.RemoteAddr().String()
}
if c.UserAgent != "" {
v[streamer.JSONUserAgent] = c.UserAgent
}
for i, media := range c.Medias {
k := "media:" + strconv.Itoa(i)
v[k] = media.String()
}
for i, track := range c.tracks {
k := "track:" + strconv.Itoa(i)
v[k] = track.String()
} }
//for i, track := range c.tracks { //for i, track := range c.tracks {
// k := "track:" + strconv.Itoa(i+1) // k := "track:" + strconv.Itoa(i+1)
// if track.MimeType() == streamer.MimeTypeH264 { // if track.MimeType() == streamer.MimeTypeH264 {
@@ -130,5 +119,6 @@ func (c *Conn) MarshalJSON() ([]byte, error) {
// v[k] = track.MimeType() // v[k] = track.MimeType()
// } // }
//} //}
return json.Marshal(v)
return json.Marshal(info)
} }

View File

@@ -3,6 +3,7 @@ package srtp
import ( import (
"encoding/binary" "encoding/binary"
"net" "net"
"sync/atomic"
) )
// Server using same UDP port for SRTP and for SRTCP as the iPhone does // Server using same UDP port for SRTP and for SRTCP as the iPhone does
@@ -55,6 +56,8 @@ func (s *Server) Serve(conn net.PacketConn) error {
} }
} }
atomic.AddUint32(&session.Recv, uint32(n))
if err = session.HandleRTP(buf[:n]); err != nil { if err = session.HandleRTP(buf[:n]); err != nil {
return err return err
} }

View File

@@ -17,6 +17,7 @@ type Session struct {
Write func(b []byte) (int, error) Write func(b []byte) (int, error)
Track *streamer.Track Track *streamer.Track
Recv uint32
lastSequence uint32 lastSequence uint32
lastTimestamp uint32 lastTimestamp uint32

View File

@@ -4,13 +4,16 @@ import (
"strings" "strings"
) )
const ( type Info struct {
JSONType = "type" Type string `json:"type,omitempty"`
JSONRemoteAddr = "remote_addr" URL string `json:"url,omitempty"`
JSONUserAgent = "user_agent" RemoteAddr string `json:"remote_addr,omitempty"`
JSONReceive = "receive" UserAgent string `json:"user_agent,omitempty"`
JSONSend = "send" Medias []*Media `json:"medias,omitempty"`
) Tracks []*Track `json:"tracks,omitempty"`
Recv uint32 `json:"recv,omitempty"`
Send uint32 `json:"send,omitempty"`
}
func Between(s, sub1, sub2 string) string { func Between(s, sub1, sub2 string) string {
i := strings.Index(s, sub1) i := strings.Index(s, sub1)

View File

@@ -1,6 +1,7 @@
package streamer package streamer
import ( import (
"encoding/json"
"fmt" "fmt"
"github.com/pion/sdp/v3" "github.com/pion/sdp/v3"
"strconv" "strconv"
@@ -70,6 +71,10 @@ func (m *Media) String() string {
return s return s
} }
func (m *Media) MarshalJSON() ([]byte, error) {
return json.Marshal(m.String())
}
func (m *Media) Clone() *Media { func (m *Media) Clone() *Media {
clone := *m clone := *m
return &clone return &clone

View File

@@ -1,6 +1,7 @@
package streamer package streamer
import ( import (
"encoding/json"
"fmt" "fmt"
"github.com/pion/rtp" "github.com/pion/rtp"
"sync" "sync"
@@ -22,12 +23,19 @@ func NewTrack(codec *Codec, direction string) *Track {
func (t *Track) String() string { func (t *Track) String() string {
s := t.Codec.String() s := t.Codec.String()
t.sinkMu.RLock() if t.sinkMu.TryRLock() {
s += fmt.Sprintf(", sinks=%d", len(t.sink)) s += fmt.Sprintf(", sinks=%d", len(t.sink))
t.sinkMu.RUnlock() t.sinkMu.RUnlock()
} else {
s += fmt.Sprintf(", sinks=?")
}
return s return s
} }
func (t *Track) MarshalJSON() ([]byte, error) {
return json.Marshal(t.String())
}
func (t *Track) WriteRTP(p *rtp.Packet) error { func (t *Track) WriteRTP(p *rtp.Packet) error {
t.sinkMu.RLock() t.sinkMu.RLock()
for _, f := range t.sink { for _, f := range t.sink {

View File

@@ -113,20 +113,12 @@ func (c *Conn) AddCandidate(candidate string) {
} }
func (c *Conn) MarshalJSON() ([]byte, error) { func (c *Conn) MarshalJSON() ([]byte, error) {
v := map[string]interface{}{ info := &streamer.Info{
streamer.JSONType: "WebRTC server consumer", Type: "WebRTC client",
streamer.JSONRemoteAddr: c.remote(), RemoteAddr: c.remote(),
UserAgent: c.UserAgent,
Recv: uint32(c.receive),
Send: uint32(c.send),
} }
return json.Marshal(info)
if c.receive > 0 {
v[streamer.JSONReceive] = c.receive
}
if c.send > 0 {
v[streamer.JSONSend] = c.send
}
if c.UserAgent != "" {
v[streamer.JSONUserAgent] = c.UserAgent
}
return json.Marshal(v)
} }

View File

@@ -137,7 +137,7 @@
tbody.innerHTML = ""; tbody.innerHTML = "";
for (const [name, value] of Object.entries(data)) { for (const [name, value] of Object.entries(data)) {
const online = value ? value.length : 0; const online = value && value.consumers ? value.consumers.length : 0;
const links = templates.map(link => { const links = templates.map(link => {
return link.replace("{name}", encodeURIComponent(name)); return link.replace("{name}", encodeURIComponent(name));
}).join(" "); }).join(" ");