mirror of
https://github.com/Monibuca/plugin-ps.git
synced 2025-12-24 12:37:59 +08:00
160 lines
3.3 KiB
Go
160 lines
3.3 KiB
Go
package mpegps
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"errors"
|
|
"io"
|
|
|
|
"m7s.live/engine/v4/util"
|
|
)
|
|
|
|
const (
|
|
StartCodePS = 0x000001ba
|
|
StartCodeSYS = 0x000001bb
|
|
StartCodeMAP = 0x000001bc
|
|
StartCodeVideo = 0x000001e0
|
|
StartCodeAudio = 0x000001c0
|
|
PrivateStreamCode = 0x000001bd
|
|
MEPGProgramEndCode = 0x000001b9
|
|
)
|
|
|
|
type EsHandler interface {
|
|
ReceiveAudio(MpegPsEsStream)
|
|
ReceiveVideo(MpegPsEsStream)
|
|
ReceivePSM(util.Buffer, bool, bool)
|
|
}
|
|
|
|
type MpegPsEsStream struct {
|
|
Type byte
|
|
util.Buffer
|
|
PTS uint32
|
|
DTS uint32
|
|
}
|
|
|
|
type MpegPsStream struct {
|
|
buffer util.Buffer
|
|
EsHandler
|
|
audio MpegPsEsStream
|
|
video MpegPsEsStream
|
|
}
|
|
|
|
func (ps *MpegPsStream) Reset() {
|
|
ps.buffer.Reset()
|
|
ps.audio.Reset()
|
|
if ps.video.Buffer.CanRead() {
|
|
ps.ReceiveVideo(ps.video)
|
|
ps.video.Buffer = make(util.Buffer, 0)
|
|
} else {
|
|
ps.video.Reset()
|
|
}
|
|
}
|
|
|
|
func (ps *MpegPsStream) Feed(data util.Buffer) (err error) {
|
|
reader := &data
|
|
if ps.buffer.CanRead() {
|
|
ps.buffer.Write(data)
|
|
reader = &ps.buffer
|
|
}
|
|
var begin util.Buffer
|
|
var payload []byte
|
|
var frame MpegPsEsStream
|
|
defer func() {
|
|
if err != nil && begin.CanRead() {
|
|
ps.buffer.Reset()
|
|
ps.buffer.Write(begin)
|
|
}
|
|
}()
|
|
for err == nil && reader.CanReadN(4) {
|
|
begin = *reader
|
|
code := reader.ReadUint32()
|
|
switch code {
|
|
case StartCodePS:
|
|
if reader.CanReadN(9) {
|
|
reader.ReadN(9)
|
|
if reader.CanRead() {
|
|
psl := reader.ReadByte() & 0x07
|
|
if reader.CanReadN(int(psl)) {
|
|
reader.ReadN(int(psl))
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
err = io.ErrShortBuffer
|
|
case StartCodeSYS, PrivateStreamCode:
|
|
_, err = ps.ReadPayload(reader)
|
|
case StartCodeMAP:
|
|
err = ps.decProgramStreamMap(reader)
|
|
case StartCodeVideo:
|
|
payload, err = ps.ReadPayload(reader)
|
|
if err == nil {
|
|
frame, err = ps.video.parsePESPacket(payload)
|
|
if err == nil {
|
|
ps.ReceiveVideo(frame)
|
|
}
|
|
}
|
|
case StartCodeAudio:
|
|
payload, err = ps.ReadPayload(reader)
|
|
if err == nil {
|
|
frame, err = ps.audio.parsePESPacket(payload)
|
|
if err == nil {
|
|
ps.ReceiveAudio(frame)
|
|
}
|
|
}
|
|
case MEPGProgramEndCode:
|
|
return
|
|
default:
|
|
err = errors.New("start code error")
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (ps *MpegPsStream) ReadPayload(data *util.Buffer) (payload []byte, err error) {
|
|
if !data.CanReadN(2) {
|
|
return nil, io.ErrShortBuffer
|
|
}
|
|
payloadlen := data.ReadUint16()
|
|
if data.CanReadN(int(payloadlen)) {
|
|
payload = data.ReadN(int(payloadlen))
|
|
} else {
|
|
err = io.ErrShortBuffer
|
|
}
|
|
return
|
|
}
|
|
|
|
func (ps *MpegPsStream) decProgramStreamMap(data *util.Buffer) error {
|
|
psm, err := ps.ReadPayload(data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
l := len(psm)
|
|
index := 2
|
|
programStreamInfoLen := binary.BigEndian.Uint16(psm[index:])
|
|
index += 2
|
|
index += int(programStreamInfoLen)
|
|
programStreamMapLen := binary.BigEndian.Uint16(psm[index:])
|
|
index += 2
|
|
for programStreamMapLen > 0 {
|
|
if l <= index+1 {
|
|
break
|
|
}
|
|
streamType := psm[index]
|
|
index++
|
|
elementaryStreamID := psm[index]
|
|
index++
|
|
if elementaryStreamID >= 0xe0 && elementaryStreamID <= 0xef {
|
|
ps.video.Type = streamType
|
|
} else if elementaryStreamID >= 0xc0 && elementaryStreamID <= 0xdf {
|
|
ps.audio.Type = streamType
|
|
}
|
|
if l <= index+1 {
|
|
break
|
|
}
|
|
elementaryStreamInfoLength := binary.BigEndian.Uint16(psm[index:])
|
|
index += 2
|
|
index += int(elementaryStreamInfoLength)
|
|
programStreamMapLen -= 4 + elementaryStreamInfoLength
|
|
}
|
|
return nil
|
|
}
|