Files
engine/track/h265.go

154 lines
4.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package track
import (
"io"
"time"
"go.uber.org/zap"
"m7s.live/engine/v4/codec"
. "m7s.live/engine/v4/common"
"m7s.live/engine/v4/util"
)
var _ SpesificTrack = (*H265)(nil)
type H265 struct {
Video
VPS []byte `json:"-"`
}
func NewH265(stream IStream, stuff ...any) (vt *H265) {
vt = &H265{}
vt.Video.CodecID = codec.CodecID_H265
vt.SetStuff("h265", int(256), byte(96), uint32(90000), stream, vt, time.Millisecond*10)
vt.SetStuff(stuff...)
vt.ParamaterSets = make(ParamaterSets, 3)
vt.dtsEst = NewDTSEstimator()
return
}
func (vt *H265) WriteSliceBytes(slice []byte) {
switch t := codec.ParseH265NALUType(slice[0]); t {
case codec.NAL_UNIT_VPS:
vt.VPS = slice
vt.ParamaterSets[0] = slice
case codec.NAL_UNIT_SPS:
vt.SPS = slice
vt.ParamaterSets[1] = slice
vt.SPSInfo, _ = codec.ParseHevcSPS(slice)
case codec.NAL_UNIT_PPS:
vt.PPS = slice
vt.ParamaterSets[2] = slice
extraData, err := codec.BuildH265SeqHeaderFromVpsSpsPps(vt.VPS, vt.SPS, vt.PPS)
if err == nil {
vt.WriteSequenceHead(extraData)
}
case
codec.NAL_UNIT_CODED_SLICE_BLA,
codec.NAL_UNIT_CODED_SLICE_BLANT,
codec.NAL_UNIT_CODED_SLICE_BLA_N_LP,
codec.NAL_UNIT_CODED_SLICE_IDR,
codec.NAL_UNIT_CODED_SLICE_IDR_N_LP,
codec.NAL_UNIT_CODED_SLICE_CRA:
vt.Value.IFrame = true
vt.AppendAuBytes(slice)
case 0, 1, 2, 3, 4, 5, 6, 7, 8, 9:
vt.Value.IFrame = false
vt.AppendAuBytes(slice)
case codec.NAL_UNIT_SEI:
vt.AppendAuBytes(slice)
default:
vt.Warn("h265 slice type not supported", zap.Uint("type", uint(t)))
}
}
func (vt *H265) WriteAVCC(ts uint32, frame *util.BLL) (err error) {
if l := frame.ByteLength; l < 6 {
vt.Error("AVCC data too short", zap.Int("len", l))
return io.ErrShortWrite
}
if frame.GetByte(1) == 0 {
vt.WriteSequenceHead(frame.ToBytes())
frame.Recycle()
if vt.VPS, vt.SPS, vt.PPS, err = codec.ParseVpsSpsPpsFromSeqHeaderWithoutMalloc(vt.SequenceHead); err == nil {
vt.SPSInfo, _ = codec.ParseHevcSPS(vt.SequenceHead)
vt.nalulenSize = (int(vt.SequenceHead[26]) & 0x03) + 1
} else {
vt.Error("H265 ParseVpsSpsPps Error")
vt.Stream.Close()
}
return
} else {
return vt.Video.WriteAVCC(ts, frame)
}
}
func (vt *H265) WriteRTPFrame(frame *RTPFrame) {
rv := &vt.Value
// TODO: DONL may need to be parsed if `sprop-max-don-diff` is greater than 0 on the RTP stream.
var usingDonlField bool
var buffer = util.Buffer(frame.Payload)
switch frame.H265Type() {
case codec.NAL_UNIT_RTP_AP:
buffer.ReadUint16()
if usingDonlField {
buffer.ReadUint16()
}
for buffer.CanRead() {
vt.WriteSliceBytes(buffer.ReadN(int(buffer.ReadUint16())))
if usingDonlField {
buffer.ReadByte()
}
}
case codec.NAL_UNIT_RTP_FU:
first3 := buffer.ReadN(3)
fuHeader := first3[2]
if usingDonlField {
buffer.ReadUint16()
}
if naluType := fuHeader & 0b00111111; util.Bit1(fuHeader, 0) {
vt.WriteSliceByte(first3[0]&0b10000001|(naluType<<1), first3[1])
}
rv.AUList.Pre.Value.Push(vt.BytesPool.GetShell(buffer))
default:
vt.WriteSliceBytes(frame.Payload)
}
frame.SequenceNumber += vt.rtpSequence //增加偏移需要增加rtp包后需要顺延
}
// RTP格式补完
func (vt *H265) CompleteRTP(value *AVFrame) {
if value.RTP.Length > 0 {
if !vt.dcChanged && value.IFrame {
vt.insertDCRtp()
}
} else {
// H265打包 https://blog.csdn.net/fanyun_01/article/details/114234290
var out [][][]byte
if value.IFrame {
out = append(out, [][]byte{vt.VPS}, [][]byte{vt.SPS}, [][]byte{vt.PPS})
}
for au := vt.Value.AUList.Next; au != nil && au != &vt.Value.AUList.ListItem; au = au.Next {
if au.Value.ByteLength < RTPMTU {
out = append(out, au.Value.ToBuffers())
} else {
var naluType codec.H265NALUType
r := au.Value.NewReader()
b0, _ := r.ReadByte()
b1, _ := r.ReadByte()
naluType = naluType.Parse(b0)
b0 = (byte(codec.NAL_UNIT_RTP_FU) << 1) | (b0 & 0b10000001)
buf := [][]byte{{b0, b1, (1 << 7) | byte(naluType)}}
buf = append(buf, r.ReadN(RTPMTU-3)...)
out = append(out, buf)
for bufs := r.ReadN(RTPMTU); len(bufs) > 0; bufs = r.ReadN(RTPMTU) {
buf = append([][]byte{{b0, b1, byte(naluType)}}, bufs...)
out = append(out, buf)
}
buf[0][2] |= 1 << 6 // set end bit
}
}
vt.PacketizeRTP(out...)
}
}