mirror of
https://github.com/AlexxIT/go2rtc.git
synced 2025-11-02 22:34:02 +08:00
Rewrite stream info API
This commit is contained in:
@@ -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)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
15
cmd/streams/consumer.go
Normal 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)
|
||||||
|
}
|
||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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")
|
||||||
}
|
|
||||||
|
|
||||||
func All() map[string]interface{} {
|
switch r.Method {
|
||||||
all := map[string]interface{}{}
|
case "PUT":
|
||||||
for name, stream := range streams {
|
name := r.URL.Query().Get("name")
|
||||||
all[name] = stream
|
if name == "" {
|
||||||
//if stream.Active() {
|
name = src
|
||||||
// all[name] = stream
|
}
|
||||||
//}
|
New(name, src)
|
||||||
|
return
|
||||||
|
case "DELETE":
|
||||||
|
delete(streams, src)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if src != "" {
|
||||||
|
e := json.NewEncoder(w)
|
||||||
|
e.SetIndent("", " ")
|
||||||
|
_ = e.Encode(streams[src])
|
||||||
|
} else {
|
||||||
|
_ = json.NewEncoder(w).Encode(streams)
|
||||||
}
|
}
|
||||||
return all
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var log zerolog.Logger
|
var log zerolog.Logger
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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)]
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(" ");
|
||||||
|
|||||||
Reference in New Issue
Block a user