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...) } }