mirror of
https://github.com/AlexxIT/go2rtc.git
synced 2025-10-06 08:46:57 +08:00
Add RTMP server and publish to RMTP logic
This commit is contained in:
163
pkg/rtmp/client.go
Normal file
163
pkg/rtmp/client.go
Normal file
@@ -0,0 +1,163 @@
|
||||
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) (core.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 flv.Open(client)
|
||||
}
|
||||
|
||||
func DialPublish(rawURL string) (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
|
||||
}
|
||||
|
||||
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]*header{},
|
||||
|
||||
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 {
|
||||
c.rdBuf = []byte{
|
||||
'F', 'L', 'V', // signature
|
||||
1, // version
|
||||
0, // flags (has video/audio)
|
||||
0, 0, 0, 9, // header size
|
||||
}
|
||||
|
||||
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
|
||||
}
|
Reference in New Issue
Block a user