install go2rtc on bob
This commit is contained in:
@@ -0,0 +1,198 @@
|
||||
package rtsp
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/AlexxIT/go2rtc/pkg/aac"
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/AlexxIT/go2rtc/pkg/h264"
|
||||
"github.com/AlexxIT/go2rtc/pkg/h265"
|
||||
"github.com/AlexxIT/go2rtc/pkg/mjpeg"
|
||||
"github.com/AlexxIT/go2rtc/pkg/pcm"
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
func (c *Conn) GetMedias() []*core.Media {
|
||||
//core.Assert(c.Medias != nil)
|
||||
return c.Medias
|
||||
}
|
||||
|
||||
func (c *Conn) AddTrack(media *core.Media, codec *core.Codec, track *core.Receiver) (err error) {
|
||||
var channel byte
|
||||
|
||||
switch c.mode {
|
||||
case core.ModeActiveProducer: // backchannel
|
||||
c.stateMu.Lock()
|
||||
defer c.stateMu.Unlock()
|
||||
|
||||
if c.state == StatePlay {
|
||||
if err = c.Reconnect(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if channel, err = c.SetupMedia(media); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
c.state = StateSetup
|
||||
|
||||
case core.ModePassiveConsumer:
|
||||
channel = byte(len(c.Senders)) * 2
|
||||
|
||||
// for consumer is better to use original track codec
|
||||
codec = track.Codec.Clone()
|
||||
// generate new payload type, starting from 96
|
||||
codec.PayloadType = byte(96 + len(c.Senders))
|
||||
|
||||
default:
|
||||
panic(core.Caller())
|
||||
}
|
||||
|
||||
// save original codec to sender (can have Codec.Name = ANY)
|
||||
sender := core.NewSender(media, codec)
|
||||
// important to send original codec for valid IsRTP check
|
||||
sender.Handler = c.packetWriter(track.Codec, channel, codec.PayloadType)
|
||||
|
||||
if c.mode == core.ModeActiveProducer && track.Codec.Name == core.CodecPCMA {
|
||||
// Fix Reolink Doorbell https://github.com/AlexxIT/go2rtc/issues/331
|
||||
sender.Handler = pcm.RepackG711(true, sender.Handler)
|
||||
}
|
||||
|
||||
sender.HandleRTP(track)
|
||||
|
||||
c.Senders = append(c.Senders, sender)
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
startVideoBuf = 32 * 1024 // 32KB
|
||||
startAudioBuf = 2 * 1024 // 2KB
|
||||
maxBuf = 1024 * 1024 // 1MB
|
||||
rtpHdr = 12 // basic RTP header size
|
||||
intHdr = 4 // interleaved header size
|
||||
)
|
||||
|
||||
func (c *Conn) packetWriter(codec *core.Codec, channel, payloadType uint8) core.HandlerFunc {
|
||||
var buf []byte
|
||||
var n int
|
||||
|
||||
video := codec.IsVideo()
|
||||
if video {
|
||||
buf = make([]byte, startVideoBuf)
|
||||
} else {
|
||||
buf = make([]byte, startAudioBuf)
|
||||
}
|
||||
|
||||
flushBuf := func() {
|
||||
//log.Printf("[rtsp] channel:%2d write_size:%6d buffer_size:%6d", channel, n, len(buf))
|
||||
if err := c.writeInterleavedData(buf[:n]); err != nil {
|
||||
c.Send += n
|
||||
}
|
||||
n = 0
|
||||
}
|
||||
|
||||
handlerFunc := func(packet *rtp.Packet) {
|
||||
if c.state == StateNone {
|
||||
return
|
||||
}
|
||||
|
||||
clone := rtp.Packet{
|
||||
Header: rtp.Header{
|
||||
Version: 2,
|
||||
Marker: packet.Marker,
|
||||
PayloadType: payloadType,
|
||||
SequenceNumber: packet.SequenceNumber,
|
||||
Timestamp: packet.Timestamp,
|
||||
SSRC: packet.SSRC,
|
||||
},
|
||||
Payload: packet.Payload,
|
||||
}
|
||||
|
||||
if !video {
|
||||
packet.Marker = true // better to have marker on all audio packets
|
||||
}
|
||||
|
||||
size := rtpHdr + len(packet.Payload)
|
||||
|
||||
if l := len(buf); n+intHdr+size > l {
|
||||
if l < maxBuf {
|
||||
buf = append(buf, make([]byte, l)...) // double buffer size
|
||||
} else {
|
||||
flushBuf()
|
||||
}
|
||||
}
|
||||
|
||||
//log.Printf("[RTP] codec: %s, size: %6d, ts: %10d, pt: %2d, ssrc: %d, seq: %d, mark: %v", codec.Name, len(packet.Payload), packet.Timestamp, packet.PayloadType, packet.SSRC, packet.SequenceNumber, packet.Marker)
|
||||
|
||||
chunk := buf[n:]
|
||||
_ = chunk[4] // bounds
|
||||
chunk[0] = '$'
|
||||
chunk[1] = channel
|
||||
chunk[2] = byte(size >> 8)
|
||||
chunk[3] = byte(size)
|
||||
|
||||
if _, err := clone.MarshalTo(chunk[4:]); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
n += 4 + size
|
||||
|
||||
if !packet.Marker || !c.playOK {
|
||||
// collect continious video packets to buffer
|
||||
// or wait OK for PLAY command for backchannel
|
||||
//log.Printf("[rtsp] collecting buffer ok=%t", c.playOK)
|
||||
return
|
||||
}
|
||||
|
||||
flushBuf()
|
||||
}
|
||||
|
||||
if !codec.IsRTP() {
|
||||
switch codec.Name {
|
||||
case core.CodecH264:
|
||||
handlerFunc = h264.RTPPay(c.PacketSize, handlerFunc)
|
||||
case core.CodecH265:
|
||||
handlerFunc = h265.RTPPay(c.PacketSize, handlerFunc)
|
||||
case core.CodecAAC:
|
||||
handlerFunc = aac.RTPPay(handlerFunc)
|
||||
case core.CodecJPEG:
|
||||
handlerFunc = mjpeg.RTPPay(handlerFunc)
|
||||
}
|
||||
} else if codec.Name == core.CodecPCML {
|
||||
handlerFunc = pcm.LittleToBig(handlerFunc)
|
||||
} else if c.PacketSize != 0 {
|
||||
switch codec.Name {
|
||||
case core.CodecH264:
|
||||
handlerFunc = h264.RTPPay(c.PacketSize, handlerFunc)
|
||||
handlerFunc = h264.RTPDepay(codec, handlerFunc)
|
||||
case core.CodecH265:
|
||||
handlerFunc = h265.RTPPay(c.PacketSize, handlerFunc)
|
||||
handlerFunc = h265.RTPDepay(codec, handlerFunc)
|
||||
}
|
||||
}
|
||||
|
||||
return handlerFunc
|
||||
}
|
||||
|
||||
func (c *Conn) writeInterleavedData(data []byte) error {
|
||||
if c.Transport != "udp" {
|
||||
_ = c.conn.SetWriteDeadline(time.Now().Add(Timeout))
|
||||
_, err := c.conn.Write(data)
|
||||
return err
|
||||
}
|
||||
|
||||
for len(data) >= 4 && data[0] == '$' {
|
||||
channel := data[1]
|
||||
size := uint16(data[2])<<8 | uint16(data[3])
|
||||
rtpData := data[4 : 4+size]
|
||||
|
||||
if _, err := c.WriteToUDP(rtpData, channel); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data = data[4+size:]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user