Files
go2rtc/pkg/mpegts/helpers.go
2023-05-04 12:26:56 +03:00

196 lines
3.6 KiB
Go

package mpegts
import (
"github.com/AlexxIT/go2rtc/pkg/core"
"github.com/AlexxIT/go2rtc/pkg/h264"
"github.com/AlexxIT/go2rtc/pkg/h265"
"github.com/pion/rtp"
"time"
)
const (
PacketSize = 188
SyncByte = 0x47
)
const (
StreamTypePrivate = 0x06 // PCMU or PCMA or FLAC from FFmpeg
StreamTypeAAC = 0x0F
StreamTypeH264 = 0x1B
StreamTypeH265 = 0x24
StreamTypePCMATapo = 0x90
)
type Packet struct {
StreamType byte
PTS time.Duration
DTS time.Duration
Payload []byte
}
// PES - Packetized Elementary Stream
type PES struct {
StreamType byte
StreamID byte
Payload []byte
Mode byte
Size int
Sequence uint16
Timestamp uint32
decodeStream func([]byte) ([]byte, int)
}
const (
ModeUnknown = iota
ModeSize
ModeStream
)
// parse Optional PES header
const minHeaderSize = 3
func (p *PES) SetBuffer(size uint16, b []byte) {
if size == 0 {
optSize := b[2] // optional fields
b = b[minHeaderSize+optSize:]
switch p.StreamType {
case StreamTypeH264:
p.Mode = ModeStream
p.decodeStream = h264.DecodeStream
case StreamTypeH265:
p.Mode = ModeStream
p.decodeStream = h265.DecodeStream
default:
println("WARNING: mpegts: unknown zero-size stream")
}
} else {
p.Mode = ModeSize
p.Size = int(size)
}
p.Payload = make([]byte, 0, size)
p.Payload = append(p.Payload, b...)
}
func (p *PES) AppendBuffer(b []byte) {
p.Payload = append(p.Payload, b...)
}
func (p *PES) GetPacket() (pkt *rtp.Packet) {
switch p.Mode {
case ModeSize:
left := p.Size - len(p.Payload)
if left > 0 {
return
}
if left < 0 {
println("WARNING: mpegts: buffer overflow")
p.Payload = nil
return
}
// fist byte also flags
flags := p.Payload[1]
optSize := p.Payload[2] // optional fields
payload := p.Payload[minHeaderSize+optSize:]
switch p.StreamType {
case StreamTypeH264, StreamTypeH265:
var ts uint32
const hasPTS = 0b1000_0000
if flags&hasPTS != 0 {
ts = ParseTime(p.Payload[minHeaderSize:])
}
pkt = &rtp.Packet{
Header: rtp.Header{
PayloadType: p.StreamType,
Timestamp: ts,
},
Payload: h264.AnnexB2AVC(payload),
}
case StreamTypePCMATapo:
p.Sequence++
p.Timestamp += uint32(len(payload))
pkt = &rtp.Packet{
Header: rtp.Header{
Version: 2,
PayloadType: p.StreamType,
SequenceNumber: p.Sequence,
Timestamp: p.Timestamp,
},
Payload: payload,
}
}
p.Payload = nil
case ModeStream:
payload, i := p.decodeStream(p.Payload)
if payload == nil {
return
}
//log.Printf("[AVC] %v, len: %d", h264.Types(payload), len(payload))
p.Payload = p.Payload[i:]
pkt = &rtp.Packet{
Header: rtp.Header{
PayloadType: p.StreamType,
Timestamp: core.Now90000(),
},
Payload: payload,
}
default:
p.Payload = nil
}
return
}
func ParseTime(b []byte) uint32 {
return (uint32(b[0]&0x0E) << 29) | (uint32(b[1]) << 22) | (uint32(b[2]&0xFE) << 14) | (uint32(b[3]) << 7) | (uint32(b[4]) >> 1)
}
func GetMedia(pkt *rtp.Packet) *core.Media {
var codec *core.Codec
var kind string
switch pkt.PayloadType {
case StreamTypeH264:
codec = &core.Codec{
Name: core.CodecH264,
ClockRate: 90000,
PayloadType: core.PayloadTypeRAW,
FmtpLine: h264.GetFmtpLine(pkt.Payload),
}
kind = core.KindVideo
case StreamTypePCMATapo:
codec = &core.Codec{
Name: core.CodecPCMA,
ClockRate: 8000,
}
kind = core.KindAudio
default:
return nil
}
return &core.Media{
Kind: kind,
Direction: core.DirectionRecvonly,
Codecs: []*core.Codec{codec},
}
}