Files
monibuca/plugin/rtmp/pkg/video.go
langhuihui 2b7672cdc2 memory leak
2024-06-14 17:13:02 +08:00

260 lines
6.4 KiB
Go

package rtmp
import (
"context"
"encoding/binary"
"time"
. "m7s.live/m7s/v5/pkg"
"m7s.live/m7s/v5/pkg/codec"
"m7s.live/m7s/v5/pkg/util"
)
var _ IAVFrame = (*RTMPVideo)(nil)
type RTMPVideo struct {
RTMPData
}
func (avcc *RTMPVideo) Parse(t *AVTrack) (isIDR, isSeq bool, raw any, err error) {
reader := avcc.NewReader()
var b0 byte
b0, err = reader.ReadByte()
if err != nil {
return
}
enhanced := b0&0b1000_0000 != 0 // https://veovera.github.io/enhanced-rtmp/docs/enhanced/enhanced-rtmp-v1.pdf
isIDR = b0&0b0111_0000>>4 == 1
packetType := b0 & 0b1111
var fourCC codec.FourCC
parseSequence := func() (err error) {
isSeq = true
isIDR = false
var cloneFrame RTMPVideo
cloneFrame.CopyFrom(&avcc.Memory)
switch fourCC {
case codec.FourCC_H264:
var ctx H264Ctx
if err = ctx.Unmarshal(reader); err == nil {
t.SequenceFrame = &cloneFrame
t.ICodecCtx = &ctx
}
case codec.FourCC_H265:
var ctx H265Ctx
if err = ctx.Unmarshal(reader); err == nil {
t.SequenceFrame = &cloneFrame
t.ICodecCtx = &ctx
}
case codec.FourCC_AV1:
var ctx AV1Ctx
if err = ctx.Unmarshal(reader); err == nil {
t.SequenceFrame = &cloneFrame
t.ICodecCtx = &ctx
}
}
return
}
if enhanced {
reader.ReadBytesTo(fourCC[:])
switch packetType {
case PacketTypeSequenceStart:
err = parseSequence()
return
case PacketTypeCodedFrames:
case PacketTypeCodedFramesX:
}
} else {
b0, err = reader.ReadByte() //sequence frame flag
if err != nil {
return
}
if VideoCodecID(b0&0x0F) == CodecID_H265 {
fourCC = codec.FourCC_H265
} else {
fourCC = codec.FourCC_H264
}
_, err = reader.ReadBE(3) // cts == 0
if err != nil {
return
}
if b0 == 0 {
if err = parseSequence(); err != nil {
return
}
} else {
// var naluLen int
// for reader.Length > 0 {
// naluLen, err = reader.ReadBE(4) // naluLenM
// if err != nil {
// return
// }
// var nalus net.Buffers
// n := reader.WriteNTo(naluLen, &nalus)
// if n != naluLen {
// err = fmt.Errorf("naluLen:%d != n:%d", naluLen, n)
// return
// }
// }
}
}
return
}
func (avcc *RTMPVideo) DecodeConfig(t *AVTrack, from ICodecCtx) (err error) {
switch fourCC := from.FourCC(); fourCC {
case codec.FourCC_H264:
h264ctx := from.(codec.IH264Ctx).GetH264Ctx()
var ctx H264Ctx
ctx.H264Ctx = *h264ctx
lenSPS := len(h264ctx.SPS[0])
lenPPS := len(h264ctx.PPS[0])
var b util.Buffer
if lenSPS > 3 {
b.Write(RTMP_AVC_HEAD[:6])
b.Write(h264ctx.SPS[0][1:4])
b.Write(RTMP_AVC_HEAD[9:10])
} else {
b.Write(RTMP_AVC_HEAD)
}
b.WriteByte(0xE1)
b.WriteUint16(uint16(lenSPS))
b.Write(h264ctx.SPS[0])
b.WriteByte(0x01)
b.WriteUint16(uint16(lenPPS))
b.Write(h264ctx.PPS[0])
t.ICodecCtx = &ctx
var seqFrame RTMPData
seqFrame.Append(b)
t.SequenceFrame = seqFrame.WrapVideo()
if t.Enabled(context.TODO(), TraceLevel) {
c := t.FourCC().String()
size := seqFrame.GetSize()
data := seqFrame.String()
t.Trace("decConfig", "codec", c, "size", size, "data", data)
}
}
return
}
func (avcc *RTMPVideo) parseH264(ctx *H264Ctx, reader *util.MemoryReader) (any, error) {
cts, err := reader.ReadBE(3)
if err != nil {
return nil, err
}
var nalus Nalus
nalus.PTS = time.Duration(avcc.Timestamp+uint32(cts)) * 90
nalus.DTS = time.Duration(avcc.Timestamp) * 90
if err := nalus.ParseAVCC(reader, ctx.NalulenSize); err != nil {
return nalus, err
}
return nalus, nil
}
func (avcc *RTMPVideo) parseH265(ctx *H265Ctx, reader *util.MemoryReader) (any, error) {
cts, err := reader.ReadBE(3)
if err != nil {
return nil, err
}
var nalus Nalus
nalus.PTS = time.Duration(avcc.Timestamp+uint32(cts)) * 90
nalus.DTS = time.Duration(avcc.Timestamp) * 90
if err := nalus.ParseAVCC(reader, ctx.NalulenSize); err != nil {
return nalus, err
}
return nalus, nil
}
func (avcc *RTMPVideo) parseAV1(reader *util.MemoryReader) (any, error) {
var obus OBUs
obus.PTS = time.Duration(avcc.Timestamp) * 90
if err := obus.ParseAVCC(reader); err != nil {
return obus, err
}
return obus, nil
}
func (avcc *RTMPVideo) ToRaw(codecCtx ICodecCtx) (any, error) {
reader := avcc.NewReader()
b0, err := reader.ReadByte()
if err != nil {
return nil, err
}
enhanced := b0&0b1000_0000 != 0 // https://veovera.github.io/enhanced-rtmp/docs/enhanced/enhanced-rtmp-v1.pdf
// frameType := b0 & 0b0111_0000 >> 4
packetType := b0 & 0b1111
if enhanced {
err = reader.Skip(4) // fourcc
if err != nil {
return nil, err
}
switch packetType {
case PacketTypeSequenceStart:
// if _, err = avcc.DecodeConfig(nil); err != nil {
// return nil, err
// }
return nil, nil
case PacketTypeCodedFrames:
if codecCtx.FourCC() == codec.FourCC_H265 {
return avcc.parseH265(codecCtx.(*H265Ctx), reader)
} else {
return avcc.parseAV1(reader)
}
case PacketTypeCodedFramesX:
}
} else {
b0, err = reader.ReadByte() //sequence frame flag
if err != nil {
return nil, err
}
if b0 == 0 {
if err = reader.Skip(3); err != nil {
return nil, err
}
// if _, err = avcc.DecodeConfig(nil); err != nil {
// return nil, err
// }
} else {
if codecCtx.FourCC() == codec.FourCC_H265 {
return avcc.parseH265(codecCtx.(*H265Ctx), reader)
} else {
return avcc.parseH264(codecCtx.(*H264Ctx), reader)
}
}
}
return nil, nil
}
func createH26xFrame(from *AVFrame, codecID VideoCodecID) (frame IAVFrame, err error) {
var rtmpVideo RTMPVideo
rtmpVideo.Timestamp = uint32(from.Timestamp / time.Millisecond)
rtmpVideo.ScalableMemoryAllocator = from.Wraps[0].GetScalableMemoryAllocator()
nalus := from.Raw.(Nalus)
rtmpVideo.RecycleIndexes = make([]int, len(nalus.Nalus)) // Recycle partial data
head := rtmpVideo.NextN(5)
head[0] = util.Conditoinal[byte](from.IDR, 0x10, 0x20) | byte(codecID)
head[1] = 1
util.PutBE(head[2:5], (nalus.PTS-nalus.DTS)/90) // cts
for _, nalu := range nalus.Nalus {
naluLenM := rtmpVideo.NextN(4)
naluLen := uint32(util.LenOfBuffers(nalu))
binary.BigEndian.PutUint32(naluLenM, naluLen)
rtmpVideo.Append(nalu...)
}
frame = &rtmpVideo
return
}
func (h264 *H264Ctx) CreateFrame(from *AVFrame) (frame IAVFrame, err error) {
return createH26xFrame(from, ParseVideoCodec(h264.FourCC()))
}
func (h265 *H265Ctx) CreateFrame(from *AVFrame) (frame IAVFrame, err error) {
return createH26xFrame(from, ParseVideoCodec(h265.FourCC()))
}
func (av1 *AV1Ctx) CreateFrame(*AVFrame) (frame IAVFrame, err error) {
return
}