mirror of
https://github.com/AlexxIT/go2rtc.git
synced 2025-10-04 16:02:43 +08:00
168 lines
3.1 KiB
Go
168 lines
3.1 KiB
Go
package rtmp
|
|
|
|
import (
|
|
"bufio"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
|
|
"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]*header{},
|
|
|
|
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 {
|
|
b := make([]byte, 1+1536)
|
|
// read C0+C1
|
|
if _, err := io.ReadFull(c.rd, b); err != nil {
|
|
return err
|
|
}
|
|
// write S0+S1, skip random
|
|
if _, err := c.conn.Write(b); err != nil {
|
|
return err
|
|
}
|
|
// read S1, skip check
|
|
if _, err := io.ReadFull(c.rd, make([]byte, 1536)); err != nil {
|
|
return err
|
|
}
|
|
// write C1
|
|
if _, err := c.conn.Write(b[1:]); err != nil {
|
|
return err
|
|
}
|
|
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"
|
|
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)
|
|
}
|
|
}
|
|
|
|
if c.App == "" {
|
|
return fmt.Errorf("rtmp: read command %x", b)
|
|
}
|
|
|
|
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:
|
|
payload := amf.EncodeItems("_result", tID, nil)
|
|
return c.writeMessage(3, TypeCommand, 0, payload)
|
|
|
|
case CommandCreateStream:
|
|
payload := amf.EncodeItems("_result", tID, nil, 1)
|
|
return c.writeMessage(3, TypeCommand, 0, payload)
|
|
|
|
case CommandPublish, CommandPlay:
|
|
c.Intent = cmd
|
|
|
|
default:
|
|
println("rtmp: unknown command: " + cmd)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *Conn) WritePlayStart() error {
|
|
payload := amf.EncodeItems("onStatus", 0, nil, map[string]any{
|
|
"code": "NetStream.Play.Start",
|
|
})
|
|
return c.writeMessage(3, TypeCommand, 0, payload)
|
|
}
|
|
|
|
func (c *Conn) code() string {
|
|
switch c.Intent {
|
|
case CommandPlay:
|
|
return "NetStream.Play.Start"
|
|
case CommandPublish:
|
|
return "NetStream.Publish.Start"
|
|
}
|
|
return ""
|
|
}
|