mirror of
https://github.com/langhuihui/monibuca.git
synced 2025-09-26 23:05:55 +08:00
356 lines
9.0 KiB
Go
356 lines
9.0 KiB
Go
package rtmp
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"io"
|
|
"net"
|
|
"time"
|
|
|
|
"github.com/deepch/vdk/codec/h264parser"
|
|
|
|
. "m7s.live/v5/pkg"
|
|
"m7s.live/v5/pkg/codec"
|
|
"m7s.live/v5/pkg/util"
|
|
)
|
|
|
|
type VideoFrame RTMPData
|
|
|
|
// 过滤掉异常的 NALU
|
|
func (avcc *VideoFrame) filterH264(naluSizeLen int) {
|
|
reader := avcc.NewReader()
|
|
lenReader := reader.NewReader()
|
|
reader.Skip(5)
|
|
var afterFilter util.Memory
|
|
lenReader.RangeN(5, afterFilter.PushOne)
|
|
allocator := avcc.GetAllocator()
|
|
var hasBadNalu bool
|
|
for {
|
|
naluLen, err := reader.ReadBE(naluSizeLen)
|
|
if err != nil {
|
|
break
|
|
}
|
|
var lenBuffer net.Buffers
|
|
lenReader.RangeN(naluSizeLen, func(b []byte) {
|
|
lenBuffer = append(lenBuffer, b)
|
|
})
|
|
lenReader.Skip(int(naluLen))
|
|
var naluBuffer net.Buffers
|
|
reader.RangeN(int(naluLen), func(b []byte) {
|
|
naluBuffer = append(naluBuffer, b)
|
|
})
|
|
badType := codec.ParseH264NALUType(naluBuffer[0][0])
|
|
// 替换之前打印 badType 的逻辑,解码并打印 SliceType
|
|
if badType == 5 { // NALU type for Coded slice of a non-IDR picture or Coded slice of an IDR picture
|
|
naluData := bytes.Join(naluBuffer, nil) // bytes 包已导入
|
|
if len(naluData) > 0 {
|
|
// h264parser 包已导入 as "github.com/deepch/vdk/codec/h264parser"
|
|
// ParseSliceHeaderFromNALU 返回的第一个值就是 SliceType
|
|
sliceType, err := h264parser.ParseSliceHeaderFromNALU(naluData)
|
|
if err == nil {
|
|
println("Decoded SliceType:", sliceType.String())
|
|
} else {
|
|
println("Error parsing H.264 slice header:", err.Error())
|
|
}
|
|
} else {
|
|
println("NALU data is empty, cannot parse H.264 slice header.")
|
|
}
|
|
}
|
|
|
|
switch badType {
|
|
case 5, 6, 7, 8, 1, 2, 3, 4:
|
|
afterFilter.Push(lenBuffer...)
|
|
afterFilter.Push(naluBuffer...)
|
|
default:
|
|
hasBadNalu = true
|
|
if allocator != nil {
|
|
for _, nalu := range lenBuffer {
|
|
allocator.Free(nalu)
|
|
}
|
|
for _, nalu := range naluBuffer {
|
|
allocator.Free(nalu)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if hasBadNalu {
|
|
avcc.Memory = afterFilter
|
|
}
|
|
}
|
|
|
|
func (avcc *VideoFrame) filterH265(naluSizeLen int) {
|
|
//TODO
|
|
}
|
|
|
|
func (avcc *VideoFrame) CheckCodecChange() (err error) {
|
|
old := avcc.ICodecCtx
|
|
if avcc.Size <= 10 {
|
|
err = io.ErrShortBuffer
|
|
return
|
|
}
|
|
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
|
|
avcc.IDR = b0&0b0111_0000>>4 == 1
|
|
packetType := b0 & 0b1111
|
|
codecId := VideoCodecID(b0 & 0x0F)
|
|
var fourCC codec.FourCC
|
|
parseSequence := func() (err error) {
|
|
avcc.IDR = false
|
|
switch fourCC {
|
|
case codec.FourCC_H264:
|
|
if old != nil && avcc.Memory.Equal(&old.(*H264Ctx).SequenceFrame.Memory) {
|
|
avcc.ICodecCtx = old
|
|
break
|
|
}
|
|
newCtx := &H264Ctx{}
|
|
newCtx.SequenceFrame.CopyFrom(&avcc.Memory)
|
|
newCtx.SequenceFrame.BaseSample = &BaseSample{}
|
|
newCtx.H264Ctx, err = codec.NewH264CtxFromRecord(newCtx.SequenceFrame.Buffers[0][reader.Offset():])
|
|
if err == nil {
|
|
avcc.ICodecCtx = newCtx
|
|
} else {
|
|
return
|
|
}
|
|
case codec.FourCC_H265:
|
|
if old != nil && avcc.Memory.Equal(&old.(*H265Ctx).SequenceFrame.Memory) {
|
|
avcc.ICodecCtx = old
|
|
break
|
|
}
|
|
newCtx := H265Ctx{
|
|
Enhanced: enhanced,
|
|
}
|
|
newCtx.SequenceFrame.CopyFrom(&avcc.Memory)
|
|
newCtx.SequenceFrame.BaseSample = &BaseSample{}
|
|
newCtx.H265Ctx, err = codec.NewH265CtxFromRecord(newCtx.SequenceFrame.Buffers[0][reader.Offset():])
|
|
if err == nil {
|
|
avcc.ICodecCtx = newCtx
|
|
} else {
|
|
return
|
|
}
|
|
case codec.FourCC_AV1:
|
|
var newCtx AV1Ctx
|
|
if err = newCtx.Unmarshal(&reader); err == nil {
|
|
avcc.ICodecCtx = &newCtx
|
|
} else {
|
|
return
|
|
}
|
|
}
|
|
return ErrSkip
|
|
}
|
|
if enhanced {
|
|
reader.Read(fourCC[:])
|
|
switch packetType {
|
|
case PacketTypeSequenceStart:
|
|
err = parseSequence()
|
|
return
|
|
case PacketTypeCodedFrames:
|
|
switch old.(type) {
|
|
case *H265Ctx:
|
|
var cts uint32
|
|
if cts, err = reader.ReadBE(3); err != nil {
|
|
return err
|
|
}
|
|
avcc.CTS = time.Duration(cts) * time.Millisecond
|
|
// avcc.filterH265(int(ctx.RecordInfo.LengthSizeMinusOne) + 1)
|
|
case *AV1Ctx:
|
|
// return avcc.parseAV1(reader)
|
|
}
|
|
case PacketTypeCodedFramesX:
|
|
// avcc.filterH265(int(old.(*H265Ctx).RecordInfo.LengthSizeMinusOne) + 1)
|
|
}
|
|
} else {
|
|
b0, err = reader.ReadByte() //sequence frame flag
|
|
if err != nil {
|
|
return
|
|
}
|
|
if codecId == CodecID_H265 {
|
|
fourCC = codec.FourCC_H265
|
|
} else {
|
|
fourCC = codec.FourCC_H264
|
|
}
|
|
var cts uint32
|
|
cts, err = reader.ReadBE(3)
|
|
if err != nil {
|
|
return
|
|
}
|
|
avcc.CTS = time.Duration(cts) * time.Millisecond
|
|
if b0 == 0 {
|
|
if err = parseSequence(); err != nil {
|
|
return
|
|
}
|
|
} else {
|
|
// switch ctx := old.(type) {
|
|
// case *codec.H264Ctx:
|
|
// avcc.filterH264(int(ctx.RecordInfo.LengthSizeMinusOne) + 1)
|
|
// case *H265Ctx:
|
|
// avcc.filterH265(int(ctx.RecordInfo.LengthSizeMinusOne) + 1)
|
|
// }
|
|
// if avcc.Size <= 5 {
|
|
// return old, ErrSkip
|
|
// }
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (avcc *VideoFrame) parseH264(ctx *H264Ctx, reader *util.MemoryReader) (err error) {
|
|
return avcc.ParseAVCC(reader, int(ctx.RecordInfo.LengthSizeMinusOne)+1)
|
|
}
|
|
|
|
func (avcc *VideoFrame) parseH265(ctx *H265Ctx, reader *util.MemoryReader) (err error) {
|
|
return avcc.ParseAVCC(reader, int(ctx.RecordInfo.LengthSizeMinusOne)+1)
|
|
}
|
|
|
|
func (avcc *VideoFrame) parseAV1(reader *util.MemoryReader) error {
|
|
var obus OBUs
|
|
if err := obus.ParseAVCC(reader); err != nil {
|
|
return err
|
|
}
|
|
avcc.Raw = &obus
|
|
return nil
|
|
}
|
|
|
|
func (avcc *VideoFrame) Demux() error {
|
|
reader := avcc.NewReader()
|
|
b0, err := reader.ReadByte()
|
|
if err != nil {
|
|
return 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 err
|
|
}
|
|
switch packetType {
|
|
case PacketTypeSequenceStart:
|
|
// see Parse()
|
|
return nil
|
|
case PacketTypeCodedFrames:
|
|
switch ctx := avcc.ICodecCtx.(type) {
|
|
case *H265Ctx:
|
|
var cts uint32
|
|
if cts, err = reader.ReadBE(3); err != nil {
|
|
return err
|
|
}
|
|
avcc.CTS = time.Duration(cts) * time.Millisecond
|
|
err = avcc.parseH265(ctx, &reader)
|
|
case *AV1Ctx:
|
|
err = avcc.parseAV1(&reader)
|
|
}
|
|
case PacketTypeCodedFramesX: // no cts
|
|
err = avcc.parseH265(avcc.ICodecCtx.(*H265Ctx), &reader)
|
|
}
|
|
return err
|
|
} else {
|
|
b0, err = reader.ReadByte() //sequence frame flag
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var cts uint32
|
|
if cts, err = reader.ReadBE(3); err != nil {
|
|
return err
|
|
}
|
|
avcc.SetCTS32(cts)
|
|
switch ctx := avcc.ICodecCtx.(type) {
|
|
case *H265Ctx:
|
|
if b0 == 0 {
|
|
// nalus.Append(ctx.VPS())
|
|
// nalus.Append(ctx.SPS())
|
|
// nalus.Append(ctx.PPS())
|
|
} else {
|
|
err = avcc.parseH265(ctx, &reader)
|
|
return err
|
|
}
|
|
|
|
case *H264Ctx:
|
|
if b0 == 0 {
|
|
// nalus.Append(ctx.SPS())
|
|
// nalus.Append(ctx.PPS())
|
|
} else {
|
|
err = avcc.parseH264(ctx, &reader)
|
|
return err
|
|
}
|
|
}
|
|
return err
|
|
}
|
|
}
|
|
|
|
func (avcc *VideoFrame) muxOld26x(codecID VideoCodecID, fromBase *Sample) {
|
|
nalus := fromBase.GetNalus()
|
|
avcc.InitRecycleIndexes(nalus.Count()) // Recycle partial data
|
|
head := avcc.NextN(5)
|
|
head[0] = util.Conditional[byte](fromBase.IDR, 0x10, 0x20) | byte(codecID)
|
|
head[1] = 1
|
|
util.PutBE(head[2:5], fromBase.CTS/time.Millisecond) // cts
|
|
for nalu := range nalus.RangePoint {
|
|
naluLenM := avcc.NextN(4)
|
|
naluLen := uint32(nalu.Size)
|
|
binary.BigEndian.PutUint32(naluLenM, naluLen)
|
|
if nalu.Size != len(util.ConcatBuffers(nalu.Buffers)) {
|
|
panic("nalu size mismatch")
|
|
}
|
|
avcc.Push(nalu.Buffers...)
|
|
}
|
|
}
|
|
|
|
func (avcc *VideoFrame) Mux(fromBase *Sample) (err error) {
|
|
switch c := fromBase.GetBase().(type) {
|
|
case *AV1Ctx:
|
|
panic(c)
|
|
case *codec.H264Ctx:
|
|
if avcc.ICodecCtx == nil {
|
|
ctx := &H264Ctx{H264Ctx: c}
|
|
ctx.SequenceFrame.PushOne(append([]byte{0x17, 0, 0, 0, 0}, c.Record...))
|
|
ctx.SequenceFrame.BaseSample = &BaseSample{}
|
|
avcc.ICodecCtx = ctx
|
|
}
|
|
avcc.muxOld26x(CodecID_H264, fromBase)
|
|
case *codec.H265Ctx:
|
|
if true {
|
|
if avcc.ICodecCtx == nil {
|
|
ctx := &H265Ctx{H265Ctx: c, Enhanced: true}
|
|
b := make(util.Buffer, len(ctx.Record)+5)
|
|
if ctx.Enhanced {
|
|
b[0] = 0b1001_0000 | byte(PacketTypeSequenceStart)
|
|
copy(b[1:], codec.FourCC_H265[:])
|
|
} else {
|
|
b[0], b[1], b[2], b[3], b[4] = 0x1C, 0, 0, 0, 0
|
|
}
|
|
copy(b[5:], ctx.Record)
|
|
ctx.SequenceFrame.PushOne(b)
|
|
ctx.SequenceFrame.BaseSample = &BaseSample{}
|
|
avcc.ICodecCtx = ctx
|
|
}
|
|
nalus := fromBase.Raw.(*Nalus)
|
|
avcc.InitRecycleIndexes(nalus.Count()) // Recycle partial data
|
|
head := avcc.NextN(8)
|
|
if fromBase.IDR {
|
|
head[0] = 0b1001_0000 | byte(PacketTypeCodedFrames)
|
|
} else {
|
|
head[0] = 0b1010_0000 | byte(PacketTypeCodedFrames)
|
|
}
|
|
copy(head[1:], codec.FourCC_H265[:])
|
|
util.PutBE(head[5:8], fromBase.CTS/time.Millisecond) // cts
|
|
for nalu := range nalus.RangePoint {
|
|
naluLenM := avcc.NextN(4)
|
|
naluLen := uint32(nalu.Size)
|
|
binary.BigEndian.PutUint32(naluLenM, naluLen)
|
|
avcc.Push(nalu.Buffers...)
|
|
}
|
|
} else {
|
|
avcc.muxOld26x(CodecID_H265, fromBase)
|
|
}
|
|
}
|
|
return
|
|
}
|