mirror of
https://github.com/Monibuca/engine.git
synced 2025-10-06 17:16:55 +08:00
154 lines
4.2 KiB
Go
154 lines
4.2 KiB
Go
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...)
|
||
}
|
||
}
|