install go2rtc on bob
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
package rtmp
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/AlexxIT/go2rtc/pkg/flv/amf"
|
||||
)
|
||||
|
||||
func NewServer(conn net.Conn) (*Conn, error) {
|
||||
c := &Conn{
|
||||
conn: conn,
|
||||
rd: bufio.NewReaderSize(conn, core.BufferSize),
|
||||
wr: conn,
|
||||
|
||||
chunks: map[uint8]*chunk{},
|
||||
|
||||
rdPacketSize: 128,
|
||||
wrPacketSize: 4096,
|
||||
}
|
||||
|
||||
if err := c.serverHandshake(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.writePacketSize(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *Conn) serverHandshake() error {
|
||||
// based on https://rtmp.veriskope.com/docs/spec/
|
||||
_ = c.conn.SetDeadline(time.Now().Add(core.ConnDeadline))
|
||||
|
||||
// read C0
|
||||
b := make([]byte, 1)
|
||||
if _, err := io.ReadFull(c.rd, b); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if b[0] != 3 {
|
||||
return errors.New("rtmp: wrong handshake")
|
||||
}
|
||||
|
||||
// write S0
|
||||
if _, err := c.conn.Write([]byte{3}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b = make([]byte, 1536)
|
||||
|
||||
// write S1
|
||||
tsS1 := nowMS()
|
||||
binary.BigEndian.PutUint32(b, tsS1)
|
||||
binary.BigEndian.PutUint32(b[4:], 0)
|
||||
_, _ = rand.Read(b[8:])
|
||||
if _, err := c.conn.Write(b); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// read C1
|
||||
if _, err := io.ReadFull(c.rd, b); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// write S2
|
||||
tsS2 := nowMS()
|
||||
binary.BigEndian.PutUint32(b, tsS1)
|
||||
binary.BigEndian.PutUint32(b[4:], tsS2)
|
||||
if _, err := c.conn.Write(b); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// read C2
|
||||
if _, err := io.ReadFull(c.rd, b); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_ = c.conn.SetDeadline(time.Time{})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) ReadCommands() error {
|
||||
for {
|
||||
msgType, _, b, err := c.readMessage()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//log.Printf("%d %.256x", msgType, b)
|
||||
|
||||
switch msgType {
|
||||
case TypeSetPacketSize:
|
||||
c.rdPacketSize = binary.BigEndian.Uint32(b)
|
||||
case TypeCommand:
|
||||
if err = c.acceptCommand(b); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.Intent != "" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
CommandConnect = "connect"
|
||||
CommandReleaseStream = "releaseStream"
|
||||
CommandFCPublish = "FCPublish"
|
||||
CommandCreateStream = "createStream"
|
||||
CommandPublish = "publish"
|
||||
CommandPlay = "play"
|
||||
)
|
||||
|
||||
func (c *Conn) acceptCommand(b []byte) error {
|
||||
items, err := amf.NewReader(b).ReadItems()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
//log.Printf("%#v", items)
|
||||
|
||||
if len(items) < 2 {
|
||||
return fmt.Errorf("rtmp: read command %x", b)
|
||||
}
|
||||
|
||||
cmd, ok := items[0].(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("rtmp: read command %x", b)
|
||||
}
|
||||
|
||||
tID, ok := items[1].(float64) // transaction ID
|
||||
if !ok {
|
||||
return fmt.Errorf("rtmp: read command %x", b)
|
||||
}
|
||||
|
||||
switch cmd {
|
||||
case CommandConnect:
|
||||
if len(items) == 3 {
|
||||
if v, ok := items[2].(map[string]any); ok {
|
||||
c.App, _ = v["app"].(string)
|
||||
}
|
||||
}
|
||||
|
||||
payload := amf.EncodeItems(
|
||||
"_result", tID,
|
||||
map[string]any{"fmsVer": "FMS/3,0,1,123"},
|
||||
map[string]any{"code": "NetConnection.Connect.Success"},
|
||||
)
|
||||
return c.writeMessage(3, TypeCommand, 0, payload)
|
||||
|
||||
case CommandReleaseStream:
|
||||
// if app is empty - will use key as app
|
||||
if c.App == "" && len(items) == 4 {
|
||||
c.App, _ = items[3].(string)
|
||||
}
|
||||
|
||||
payload := amf.EncodeItems("_result", tID, nil)
|
||||
return c.writeMessage(3, TypeCommand, 0, payload)
|
||||
|
||||
case CommandFCPublish: // no response
|
||||
|
||||
case CommandCreateStream:
|
||||
payload := amf.EncodeItems("_result", tID, nil, 1)
|
||||
return c.writeMessage(3, TypeCommand, 0, payload)
|
||||
|
||||
case CommandPublish, CommandPlay: // response later
|
||||
c.Intent = cmd
|
||||
c.streamID = 1
|
||||
|
||||
default:
|
||||
println("rtmp: unknown command: " + cmd)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) WriteStart() error {
|
||||
var code string
|
||||
if c.Intent == CommandPublish {
|
||||
code = "NetStream.Publish.Start"
|
||||
} else {
|
||||
code = "NetStream.Play.Start"
|
||||
}
|
||||
|
||||
payload := amf.EncodeItems("onStatus", 0, nil, map[string]any{"code": code})
|
||||
return c.writeMessage(3, TypeCommand, 0, payload)
|
||||
}
|
||||
|
||||
func nowMS() uint32 {
|
||||
return uint32(time.Now().UnixNano() / int64(time.Millisecond))
|
||||
}
|
||||
Reference in New Issue
Block a user