mirror of
https://github.com/langhuihui/monibuca.git
synced 2025-09-27 01:15:52 +08:00
342 lines
8.6 KiB
Go
342 lines
8.6 KiB
Go
package gb28181
|
||
|
||
import (
|
||
"errors"
|
||
"fmt"
|
||
"net"
|
||
"os"
|
||
"strings"
|
||
|
||
"github.com/pion/rtp"
|
||
"m7s.live/v5"
|
||
"m7s.live/v5/pkg"
|
||
"m7s.live/v5/pkg/task"
|
||
"m7s.live/v5/pkg/util"
|
||
"m7s.live/v5/plugin/gb28181/udputils"
|
||
rtp2 "m7s.live/v5/plugin/rtp/pkg"
|
||
)
|
||
|
||
const (
|
||
StartCodePS = 0x000001ba
|
||
StartCodeSYS = 0x000001bb
|
||
StartCodeMAP = 0x000001bc
|
||
StartCodeVideo = 0x000001e0
|
||
StartCodeAudio = 0x000001c0
|
||
PrivateStreamCode = 0x000001bd
|
||
MEPGProgramEndCode = 0x000001b9
|
||
)
|
||
|
||
type PSPublisher struct {
|
||
*m7s.Publisher
|
||
*util.BufReader
|
||
Receiver Receiver
|
||
}
|
||
|
||
var ErrRTPReceiveLost = errors.New("rtp receive lost")
|
||
|
||
type Receiver struct {
|
||
task.Task
|
||
rtp.Packet
|
||
FeedChan chan []byte
|
||
psm util.Memory
|
||
dump *os.File
|
||
dumpLen []byte
|
||
psVideo PSVideo
|
||
psAudio PSAudio
|
||
RTPReader *rtp2.TCP
|
||
ListenAddr string
|
||
Listener net.Listener
|
||
StreamMode string // 数据流传输模式(UDP:udp传输/TCP-ACTIVE:tcp主动模式/TCP-PASSIVE:tcp被动模式)
|
||
SSRC uint32 // RTP SSRC
|
||
ListenerUdp *net.UDPConn
|
||
RTPReaderUdp *rtp2.UDP
|
||
IsSinglePort bool
|
||
SingleStop chan struct{}
|
||
udpCache *udputils.PriorityQueueRtp
|
||
UdpCacheSize int
|
||
lastSeq uint16
|
||
}
|
||
|
||
func NewPSPublisher(puber *m7s.Publisher) *PSPublisher {
|
||
ret := &PSPublisher{
|
||
Publisher: puber,
|
||
}
|
||
ret.Receiver.FeedChan = make(chan []byte, 10)
|
||
ret.BufReader = util.NewBufReaderChan(ret.Receiver.FeedChan)
|
||
ret.Receiver.psVideo.SetAllocator(ret.Allocator)
|
||
ret.Receiver.psAudio.SetAllocator(ret.Allocator)
|
||
return ret
|
||
}
|
||
|
||
func (p *PSPublisher) ReadPayload() (payload util.Memory, err error) {
|
||
payloadlen, err := p.ReadBE(2)
|
||
if err != nil {
|
||
return
|
||
}
|
||
return p.ReadBytes(payloadlen)
|
||
}
|
||
|
||
func (p *PSPublisher) Demux() {
|
||
var payload util.Memory
|
||
defer p.Info("demux exit")
|
||
for {
|
||
code, err := p.ReadBE32(4)
|
||
if err != nil {
|
||
return
|
||
}
|
||
p.Trace("demux", "code", code)
|
||
switch code {
|
||
case StartCodePS:
|
||
var psl byte
|
||
if err = p.Skip(9); err != nil {
|
||
return
|
||
}
|
||
psl, err = p.ReadByte()
|
||
if err != nil {
|
||
return
|
||
}
|
||
psl &= 0x07
|
||
if err = p.Skip(int(psl)); err != nil {
|
||
return
|
||
}
|
||
case StartCodeVideo:
|
||
payload, err = p.ReadPayload()
|
||
var annexB *pkg.AnnexB
|
||
annexB, err = p.Receiver.psVideo.parsePESPacket(payload)
|
||
if annexB != nil {
|
||
err = p.WriteVideo(annexB)
|
||
}
|
||
case StartCodeAudio:
|
||
payload, err = p.ReadPayload()
|
||
var audioFrame pkg.IAVFrame
|
||
audioFrame, err = p.Receiver.psAudio.parsePESPacket(payload)
|
||
if audioFrame != nil {
|
||
err = p.WriteAudio(audioFrame)
|
||
}
|
||
case StartCodeMAP:
|
||
p.decProgramStreamMap()
|
||
case StartCodeSYS, PrivateStreamCode:
|
||
p.ReadPayload()
|
||
default:
|
||
p.ReadPayload()
|
||
}
|
||
}
|
||
}
|
||
|
||
func (dec *PSPublisher) decProgramStreamMap() (err error) {
|
||
dec.Receiver.psm, err = dec.ReadPayload()
|
||
if err != nil {
|
||
return err
|
||
}
|
||
var programStreamInfoLen, programStreamMapLen, elementaryStreamInfoLength uint32
|
||
var streamType, elementaryStreamID byte
|
||
reader := dec.Receiver.psm.NewReader()
|
||
reader.Skip(2)
|
||
programStreamInfoLen, err = reader.ReadBE(2)
|
||
reader.Skip(int(programStreamInfoLen))
|
||
programStreamMapLen, err = reader.ReadBE(2)
|
||
for programStreamMapLen > 0 {
|
||
streamType, err = reader.ReadByte()
|
||
elementaryStreamID, err = reader.ReadByte()
|
||
if elementaryStreamID >= 0xe0 && elementaryStreamID <= 0xef {
|
||
dec.Receiver.psVideo.streamType = streamType
|
||
} else if elementaryStreamID >= 0xc0 && elementaryStreamID <= 0xdf {
|
||
dec.Receiver.psAudio.streamType = streamType
|
||
}
|
||
elementaryStreamInfoLength, err = reader.ReadBE(2)
|
||
reader.Skip(int(elementaryStreamInfoLength))
|
||
programStreamMapLen -= 4 + elementaryStreamInfoLength
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func (p *PSPublisher) GetKey() uint32 {
|
||
return p.Receiver.SSRC
|
||
}
|
||
|
||
func (p *Receiver) ReadRTP(rtp util.Buffer) (err error) {
|
||
lastSeq := p.SequenceNumber
|
||
if err = p.Unmarshal(rtp); err != nil {
|
||
p.Error("unmarshal error", "err", err)
|
||
return
|
||
}
|
||
|
||
// 如果设置了SSRC过滤,只处理匹配的SSRC
|
||
if p.SSRC != 0 && p.SSRC != p.Packet.SSRC {
|
||
p.Info("into single port mode, ssrc mismatch", "expected", p.SSRC, "actual", p.Packet.SSRC)
|
||
if p.TraceEnabled() {
|
||
p.Trace("rtp ssrc mismatch, skip", "expected", p.SSRC, "actual", p.Packet.SSRC)
|
||
}
|
||
return nil
|
||
}
|
||
|
||
if lastSeq == 0 || p.SequenceNumber == lastSeq+1 {
|
||
if p.TraceEnabled() {
|
||
p.Trace("rtp", "len", rtp.Len(), "seq", p.SequenceNumber, "payloadType", p.PayloadType, "ssrc", p.Packet.SSRC)
|
||
}
|
||
copyData := make([]byte, len(p.Payload))
|
||
copy(copyData, p.Payload)
|
||
select {
|
||
case p.FeedChan <- copyData:
|
||
// 成功发送数据
|
||
case <-p.Done():
|
||
// 任务已停止,返回错误
|
||
return task.ErrTaskComplete
|
||
}
|
||
return
|
||
} else {
|
||
p.Error("rtp seq mismatch,", "lastSeq", lastSeq, "seq", p.SequenceNumber)
|
||
return ErrRTPReceiveLost
|
||
}
|
||
|
||
}
|
||
|
||
func (p *Receiver) ReadUdpRTP(rtp util.Buffer) (err error) {
|
||
//解析rtp
|
||
if err = p.Unmarshal(rtp); err != nil {
|
||
p.Error("unmarshal error", "err", err)
|
||
return nil
|
||
}
|
||
//判断ssrc
|
||
if p.SSRC != 0 && p.SSRC != p.Packet.SSRC {
|
||
p.Info("ReadUdpRTP, ssrc mismatch", "expected", p.SSRC, "actual", p.Packet.SSRC)
|
||
if p.TraceEnabled() {
|
||
p.Trace("rtp ssrc mismatch, skip", "expected", p.SSRC, "actual", p.Packet.SSRC)
|
||
}
|
||
return nil
|
||
}
|
||
|
||
if p.UdpCacheSize > 0 && p.udpCache == nil {
|
||
p.udpCache = udputils.NewPqRtp()
|
||
}
|
||
//p.Info("ReadUdpRTP, seq", "rtpSeq", p.SequenceNumber)
|
||
//加入缓存,自动排序
|
||
rtpTmpCache := p.Packet
|
||
rtpTmpCache.Payload = make([]byte, len(p.Payload))
|
||
copy(rtpTmpCache.Payload, p.Payload)
|
||
p.udpCache.Push(rtpTmpCache)
|
||
|
||
rtpTmp := p.Packet
|
||
if p.udpCache.Len() < p.UdpCacheSize-1 {
|
||
return nil
|
||
} else {
|
||
rtpTmp, _ = p.udpCache.Pop()
|
||
}
|
||
|
||
//p.Info("ReadUdpRTP, seq", "rtpTmpSeq", rtpTmp.SequenceNumber)
|
||
|
||
p.lastSeq = rtpTmp.SequenceNumber
|
||
|
||
if p.TraceEnabled() {
|
||
p.Trace("rtp", "len", rtp.Len(), "seq", p.SequenceNumber, "payloadType", p.PayloadType, "ssrc", p.Packet.SSRC)
|
||
}
|
||
|
||
copyData := make([]byte, len(rtpTmp.Payload))
|
||
copy(copyData, rtpTmp.Payload)
|
||
select {
|
||
case p.FeedChan <- copyData:
|
||
// 成功发送数据
|
||
case <-p.Done():
|
||
// 任务已停止,返回错误
|
||
return task.ErrTaskComplete
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func (p *Receiver) Start() (err error) {
|
||
if strings.ToUpper(p.StreamMode) == "TCP-ACTIVE" {
|
||
// TCP主动模式不需要监听,直接返回
|
||
p.Info("TCP-ACTIVE mode, no need to listen")
|
||
return nil
|
||
} else if strings.ToUpper(p.StreamMode) == "TCP-PASSIVE" {
|
||
// TCP被动模式
|
||
if p.Listener == nil {
|
||
p.Info("start new listener", "addr", p.ListenAddr)
|
||
p.Listener, err = net.Listen("tcp4", p.ListenAddr)
|
||
if err != nil {
|
||
p.Error("start listen", "err", err)
|
||
return errors.New("start listen,err" + err.Error())
|
||
}
|
||
}
|
||
p.Info("start listen", "addr", p.ListenAddr)
|
||
} else {
|
||
if p.ListenerUdp == nil {
|
||
p.Info("start new listener", "addr", p.ListenAddr)
|
||
|
||
p.ListenerUdp, err = util.ListenUDP(p.ListenAddr, 1024*1024*10)
|
||
if err != nil {
|
||
p.Error("start listen", "err", err)
|
||
return errors.New("start listen,err" + err.Error())
|
||
}
|
||
}
|
||
}
|
||
return
|
||
}
|
||
|
||
func (p *Receiver) Dispose() {
|
||
if p.SSRC == 0 {
|
||
p.Info("into multiport mode ,close listener ", p.SSRC)
|
||
if p.Listener != nil {
|
||
p.Listener.Close()
|
||
}
|
||
}
|
||
if p.RTPReader != nil {
|
||
p.RTPReader.Close()
|
||
}
|
||
if p.ListenerUdp != nil && !p.IsSinglePort {
|
||
p.ListenerUdp.Close()
|
||
}
|
||
if p.IsSinglePort {
|
||
close(p.SingleStop)
|
||
}
|
||
|
||
if p.FeedChan != nil {
|
||
close(p.FeedChan)
|
||
}
|
||
}
|
||
|
||
func (p *Receiver) Go() error {
|
||
if strings.ToUpper(p.StreamMode) == "TCP-ACTIVE" {
|
||
// TCP主动模式,主动连接设备
|
||
addr := p.ListenAddr
|
||
if !strings.Contains(addr, ":") {
|
||
addr = ":" + addr
|
||
}
|
||
if strings.HasPrefix(addr, ":") {
|
||
p.Error("invalid address, missing IP", "addr", addr)
|
||
return fmt.Errorf("invalid address %s, missing IP", addr)
|
||
}
|
||
p.Info("TCP-ACTIVE mode, connecting to device", "addr", addr)
|
||
conn, err := net.Dial("tcp", addr)
|
||
if err != nil {
|
||
p.Error("connect to device failed", "err", err)
|
||
return err
|
||
}
|
||
p.RTPReader = (*rtp2.TCP)(conn.(*net.TCPConn))
|
||
p.Info("connected to device", "addr", conn.RemoteAddr())
|
||
return p.RTPReader.Read(p.ReadRTP)
|
||
} else if strings.ToUpper(p.StreamMode) == "TCP-PASSIVE" { // TCP被动模式
|
||
p.Info("start accept")
|
||
conn, err := p.Listener.Accept()
|
||
if err != nil {
|
||
p.Error("accept", "err", err)
|
||
return err
|
||
}
|
||
p.RTPReader = (*rtp2.TCP)(conn.(*net.TCPConn))
|
||
p.Info("accept", "addr", conn.RemoteAddr())
|
||
return p.RTPReader.Read(p.ReadRTP)
|
||
} else { //UDP模式
|
||
if p.IsSinglePort {
|
||
p.SingleStop = make(chan struct{})
|
||
<-p.SingleStop
|
||
p.Info("stop udp accept", "ssrc", p.SSRC)
|
||
return nil
|
||
|
||
} else {
|
||
p.Info("start udp accept")
|
||
p.RTPReaderUdp = (*rtp2.UDP)(p.ListenerUdp)
|
||
return p.RTPReaderUdp.Read(p.ReadUdpRTP)
|
||
}
|
||
}
|
||
}
|