Files
monibuca/pkg/annexb.go
2025-01-08 15:27:06 +08:00

183 lines
4.3 KiB
Go

package pkg
import (
"encoding/binary"
"fmt"
"io"
"time"
"github.com/deepch/vdk/codec/h264parser"
"github.com/deepch/vdk/codec/h265parser"
"m7s.live/v5/pkg/codec"
"m7s.live/v5/pkg/util"
)
var _ IAVFrame = (*AnnexB)(nil)
type AnnexB struct {
Hevc bool
PTS time.Duration
DTS time.Duration
util.RecyclableMemory
}
func (a *AnnexB) Dump(t byte, w io.Writer) {
m := a.GetAllocator().Borrow(4 + a.Size)
binary.BigEndian.PutUint32(m, uint32(a.Size))
a.CopyTo(m[4:])
w.Write(m)
}
// DecodeConfig implements pkg.IAVFrame.
func (a *AnnexB) ConvertCtx(ctx codec.ICodecCtx) (codec.ICodecCtx, IAVFrame, error) {
return ctx.GetBase(), nil, nil
}
// GetSize implements pkg.IAVFrame.
func (a *AnnexB) GetSize() int {
return a.Size
}
func (a *AnnexB) GetTimestamp() time.Duration {
return a.DTS * time.Millisecond / 90
}
func (a *AnnexB) GetCTS() time.Duration {
return (a.PTS - a.DTS) * time.Millisecond / 90
}
// Parse implements pkg.IAVFrame.
func (a *AnnexB) Parse(t *AVTrack) (err error) {
if a.Hevc {
if t.ICodecCtx == nil {
t.ICodecCtx = &codec.H265Ctx{}
}
} else {
if t.ICodecCtx == nil {
t.ICodecCtx = &codec.H264Ctx{}
}
}
if t.Value.Raw, err = a.Demux(t.ICodecCtx); err != nil {
return
}
for _, nalu := range t.Value.Raw.(Nalus) {
if a.Hevc {
ctx := t.ICodecCtx.(*codec.H265Ctx)
switch codec.ParseH265NALUType(nalu.Buffers[0][0]) {
case h265parser.NAL_UNIT_VPS:
ctx.RecordInfo.VPS = [][]byte{nalu.ToBytes()}
case h265parser.NAL_UNIT_SPS:
ctx.RecordInfo.SPS = [][]byte{nalu.ToBytes()}
case h265parser.NAL_UNIT_PPS:
ctx.RecordInfo.PPS = [][]byte{nalu.ToBytes()}
ctx.CodecData, err = h265parser.NewCodecDataFromVPSAndSPSAndPPS(ctx.VPS(), ctx.SPS(), ctx.PPS())
case h265parser.NAL_UNIT_CODED_SLICE_BLA_W_LP,
h265parser.NAL_UNIT_CODED_SLICE_BLA_W_RADL,
h265parser.NAL_UNIT_CODED_SLICE_BLA_N_LP,
h265parser.NAL_UNIT_CODED_SLICE_IDR_W_RADL,
h265parser.NAL_UNIT_CODED_SLICE_IDR_N_LP,
h265parser.NAL_UNIT_CODED_SLICE_CRA:
t.Value.IDR = true
}
} else {
ctx := t.ICodecCtx.(*codec.H264Ctx)
switch codec.ParseH264NALUType(nalu.Buffers[0][0]) {
case codec.NALU_SPS:
ctx.RecordInfo.SPS = [][]byte{nalu.ToBytes()}
if len(ctx.RecordInfo.PPS) > 0 {
ctx.CodecData, err = h264parser.NewCodecDataFromSPSAndPPS(ctx.SPS(), ctx.PPS())
}
case codec.NALU_PPS:
ctx.RecordInfo.PPS = [][]byte{nalu.ToBytes()}
if len(ctx.RecordInfo.SPS) > 0 {
ctx.CodecData, err = h264parser.NewCodecDataFromSPSAndPPS(ctx.SPS(), ctx.PPS())
}
case codec.NALU_IDR_Picture:
t.Value.IDR = true
}
}
}
return
}
// String implements pkg.IAVFrame.
func (a *AnnexB) String() string {
return fmt.Sprintf("%d %d", a.DTS, a.Memory.Size)
}
// Demux implements pkg.IAVFrame.
func (a *AnnexB) Demux(codecCtx codec.ICodecCtx) (ret any, err error) {
var nalus Nalus
var lastFourBytes [4]byte
var b byte
var shallow util.Memory
shallow.Append(a.Buffers...)
reader := shallow.NewReader()
gotNalu := func() {
var nalu util.Memory
for buf := range reader.ClipFront {
nalu.AppendOne(buf)
}
nalus = append(nalus, nalu)
}
for {
b, err = reader.ReadByte()
if err == nil {
copy(lastFourBytes[:], lastFourBytes[1:])
lastFourBytes[3] = b
var startCode = 0
if lastFourBytes == codec.NALU_Delimiter2 {
startCode = 4
} else if [3]byte(lastFourBytes[1:]) == codec.NALU_Delimiter1 {
startCode = 3
}
if startCode > 0 && reader.Offset() >= 3 {
if reader.Offset() == 3 {
startCode = 3
}
reader.Unread(startCode)
if reader.Offset() > 0 {
gotNalu()
}
reader.Skip(startCode)
for range reader.ClipFront {
}
}
} else if err == io.EOF {
if reader.Offset() > 0 {
gotNalu()
}
err = nil
break
}
}
ret = nalus
return
}
func (a *AnnexB) Mux(codecCtx codec.ICodecCtx, frame *AVFrame) {
a.DTS = frame.Timestamp * 90 / time.Millisecond
a.PTS = a.DTS + frame.CTS*90/time.Millisecond
a.InitRecycleIndexes(0)
delimiter2 := codec.NALU_Delimiter2[:]
a.AppendOne(delimiter2)
if frame.IDR {
switch ctx := codecCtx.(type) {
case *codec.H264Ctx:
a.Append(ctx.SPS(), delimiter2, ctx.PPS(), delimiter2)
case *codec.H265Ctx:
a.Append(ctx.SPS(), delimiter2, ctx.PPS(), delimiter2, ctx.VPS(), delimiter2)
}
}
for i, nalu := range frame.Raw.(Nalus) {
if i > 0 {
a.AppendOne(codec.NALU_Delimiter1[:])
}
a.Append(nalu.Buffers...)
}
}