diff --git a/go2rtc.exe b/go2rtc.exe new file mode 100644 index 00000000..560eac48 Binary files /dev/null and b/go2rtc.exe differ diff --git a/internal/outputbc/init.go b/internal/outputbc/init.go new file mode 100644 index 00000000..7003b8bc --- /dev/null +++ b/internal/outputbc/init.go @@ -0,0 +1,22 @@ +package outputbc + +import ( + "github.com/AlexxIT/go2rtc/internal/streams" + "github.com/AlexxIT/go2rtc/pkg/core" + "github.com/AlexxIT/go2rtc/pkg/outputbc" + "github.com/AlexxIT/go2rtc/pkg/shell" +) + +func Init() { + streams.HandleFunc("outputbc", handle) +} + +func handle(url string) (core.Producer, error) { + args := shell.QuoteSplit(url[9:]) + con, err := outputbc.NewClient(args) + if err != nil { + return nil, err + } + con.Dial() + return con, nil +} diff --git a/main.go b/main.go index 91bc9938..bef3f104 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,8 @@ package main import ( + "github.com/AlexxIT/go2rtc/internal/outputbc" + "github.com/AlexxIT/go2rtc/internal/api" "github.com/AlexxIT/go2rtc/internal/api/ws" "github.com/AlexxIT/go2rtc/internal/app" @@ -80,6 +82,7 @@ func main() { bubble.Init() // bubble source expr.Init() // expr source gopro.Init() // gopro source + outputbc.Init() // 6. Helper modules diff --git a/pkg/outputbc/client.go b/pkg/outputbc/client.go new file mode 100644 index 00000000..8c0295c5 --- /dev/null +++ b/pkg/outputbc/client.go @@ -0,0 +1,56 @@ +package outputbc + +import ( + "io" + "net" + "os/exec" + "sync" + + "github.com/AlexxIT/go2rtc/pkg/core" +) + +type Client struct { + medias []*core.Media + sender *core.Sender + conn net.Conn + send int + cmd exec.Cmd + pipe io.WriteCloser + command []string +} + +var lock = &sync.Mutex{} +var singleInstance *Client + +func NewClient(command []string) (*Client, error) { + return &Client{command: command}, nil +} + +func (c *Client) Dial() { + media := &core.Media{ + Kind: core.KindAudio, + Direction: core.DirectionSendonly, + Codecs: []*core.Codec{ + {Name: core.CodecPCMA, ClockRate: 8000}, + }, + } + + c.medias = append(c.medias, media) + if c.pipe == nil { + cmdName := c.command[0] + args := c.command[1:] + c.cmd = *exec.Command(cmdName, args...) + c.pipe, _ = c.cmd.StdinPipe() + } +} + +func (c *Client) Open() (err error) { + c.cmd.Run() + return +} + +func (c *Client) Close() (err error) { + c.pipe.Close() + c.cmd.Process.Kill() + return +} diff --git a/pkg/outputbc/consumer.go b/pkg/outputbc/consumer.go new file mode 100644 index 00000000..ba5d2ea2 --- /dev/null +++ b/pkg/outputbc/consumer.go @@ -0,0 +1,62 @@ +package outputbc + +import ( + "encoding/json" + + "github.com/AlexxIT/go2rtc/pkg/core" + "github.com/pion/rtp" +) + +func (c *Client) GetMedias() []*core.Media { + return c.medias +} + +func (c *Client) GetTrack(media *core.Media, codec *core.Codec) (*core.Receiver, error) { + return nil, core.ErrCantGetTrack +} + +func (c *Client) AddTrack(media *core.Media, _ *core.Codec, track *core.Receiver) error { + if c.sender == nil { + c.sender = core.NewSender(media, track.Codec) + c.sender.Handler = func(packet *rtp.Packet) { + c.pipe.Write(packet.Payload) + + c.send += len(packet.Payload) + } + } + + c.sender.HandleRTP(track) + return nil +} + +func (c *Client) Start() (err error) { + if err = c.Open(); err != nil { + return + } + return +} + +func (c *Client) Stop() (err error) { + if c.sender != nil { + c.sender.Close() + } + + if c.conn != nil { + _ = c.Close() + return c.conn.Close() + } + + return nil +} + +func (c *Client) MarshalJSON() ([]byte, error) { + info := &core.Info{ + Type: "Command Backchannel PCMA", + Medias: c.medias, + Send: c.send, + } + if c.sender != nil { + info.Senders = []*core.Sender{c.sender} + } + return json.Marshal(info) +}