package rtmp import ( "bufio" "io" "net" "net/url" "strings" "github.com/AlexxIT/go2rtc/pkg/core" "github.com/AlexxIT/go2rtc/pkg/flv" "github.com/AlexxIT/go2rtc/pkg/tcp" ) func DialPlay(rawURL string) (*flv.Producer, error) { u, err := url.Parse(rawURL) if err != nil { return nil, err } conn, err := tcp.Dial(u, core.ConnDialTimeout) if err != nil { return nil, err } client, err := NewClient(conn, u) if err != nil { return nil, err } if err = client.play(); err != nil { return nil, err } return client.Producer() } func DialPublish(rawURL string, cons *flv.Consumer) (io.Writer, error) { u, err := url.Parse(rawURL) if err != nil { return nil, err } conn, err := tcp.Dial(u, core.ConnDialTimeout) if err != nil { return nil, err } client, err := NewClient(conn, u) if err != nil { return nil, err } if err = client.publish(); err != nil { return nil, err } cons.FormatName = "rtmp" cons.Protocol = "rtmp" cons.RemoteAddr = conn.RemoteAddr().String() cons.URL = rawURL return client, nil } func NewClient(conn net.Conn, u *url.URL) (*Conn, error) { c := &Conn{ url: u.String(), conn: conn, rd: bufio.NewReaderSize(conn, core.BufferSize), wr: conn, chunks: map[uint8]*chunk{}, rdPacketSize: 128, wrPacketSize: 4096, // OBS - 4096, Reolink - 4096 } if args := strings.Split(u.Path, "/"); len(args) >= 2 { c.App = args[1] if len(args) >= 3 { c.Stream = args[2] if u.RawQuery != "" { c.Stream += "?" + u.RawQuery } } } if err := c.clienHandshake(); err != nil { return nil, err } if err := c.writePacketSize(); err != nil { return nil, err } return c, nil } func (c *Conn) clienHandshake() error { // simple handshake without real random and check response b := make([]byte, 1+1536) b[0] = 0x03 // write C0+C1 if _, err := c.conn.Write(b); err != nil { return err } // read S0+S1 if _, err := io.ReadFull(c.rd, b); err != nil { return err } // write S1 if _, err := c.conn.Write(b[1:]); err != nil { return err } // read C1, skip check if _, err := io.ReadFull(c.rd, b[1:]); err != nil { return err } return nil } func (c *Conn) play() error { if err := c.writeConnect(); err != nil { return err } if err := c.writeCreateStream(); err != nil { return err } if err := c.writePlay(); err != nil { return err } return nil } func (c *Conn) publish() error { if err := c.writeConnect(); err != nil { return err } if err := c.writeReleaseStream(); err != nil { return err } if err := c.writeCreateStream(); err != nil { return err } if err := c.writePublish(); err != nil { return err } go func() { for { _, _, _, err := c.readMessage() //log.Printf("!!! %d %d %.30x", msgType, timeMS, b) if err != nil { return } } }() return nil }