mirror of
https://github.com/Monibuca/engine.git
synced 2025-10-08 10:00:10 +08:00
194 lines
5.7 KiB
Go
194 lines
5.7 KiB
Go
package track
|
||
|
||
import (
|
||
"net"
|
||
"time"
|
||
|
||
"go.uber.org/zap"
|
||
"m7s.live/engine/v4/codec"
|
||
. "m7s.live/engine/v4/common"
|
||
"m7s.live/engine/v4/config"
|
||
"m7s.live/engine/v4/util"
|
||
)
|
||
|
||
type H265 struct {
|
||
Video
|
||
}
|
||
|
||
func NewH265(stream IStream) (vt *H265) {
|
||
vt = &H265{}
|
||
vt.Video.CodecID = codec.CodecID_H265
|
||
vt.Video.DecoderConfiguration.Raw = make(NALUSlice, 3)
|
||
vt.SetStuff("h265", stream, int(256), byte(96), uint32(90000), vt, time.Millisecond*10)
|
||
vt.dtsEst = NewDTSEstimator()
|
||
return
|
||
}
|
||
func (vt *H265) WriteAnnexB(pts uint32, dts uint32, frame AnnexBFrame) {
|
||
if dts == 0 {
|
||
vt.generateTimestamp(pts)
|
||
} else {
|
||
vt.Video.Media.RingBuffer.Value.PTS = pts
|
||
vt.Video.Media.RingBuffer.Value.DTS = dts
|
||
}
|
||
// println(pts,dts,len(frame))
|
||
for _, slice := range vt.Video.WriteAnnexB(frame) {
|
||
vt.WriteSlice(slice)
|
||
}
|
||
if len(vt.Value.Raw) > 0 {
|
||
vt.Flush()
|
||
}
|
||
}
|
||
func (vt *H265) WriteSlice(slice NALUSlice) {
|
||
// println(slice.H265Type())
|
||
switch slice.H265Type() {
|
||
case codec.NAL_UNIT_VPS:
|
||
vt.Video.DecoderConfiguration.Raw[0] = slice[0]
|
||
case codec.NAL_UNIT_SPS:
|
||
vt.Video.DecoderConfiguration.Raw[1] = slice[0]
|
||
vt.Video.SPSInfo, _ = codec.ParseHevcSPS(slice[0])
|
||
case codec.NAL_UNIT_PPS:
|
||
vt.Video.dcChanged = true
|
||
vt.Video.DecoderConfiguration.Raw[2] = slice[0]
|
||
extraData, err := codec.BuildH265SeqHeaderFromVpsSpsPps(vt.Video.DecoderConfiguration.Raw[0], vt.Video.DecoderConfiguration.Raw[1], vt.Video.DecoderConfiguration.Raw[2])
|
||
if err == nil {
|
||
vt.Video.DecoderConfiguration.AVCC = net.Buffers{extraData}
|
||
}
|
||
vt.Video.DecoderConfiguration.Seq++
|
||
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.Video.WriteSlice(slice)
|
||
case 0, 1, 2, 3, 4, 5, 6, 7, 8, 9:
|
||
vt.Value.IFrame = false
|
||
vt.Video.WriteSlice(slice)
|
||
case codec.NAL_UNIT_SEI:
|
||
vt.Value.SEI = slice
|
||
default:
|
||
vt.Video.Stream.Warn("h265 slice type not supported", zap.Uint("type", uint(slice.H265Type())))
|
||
}
|
||
}
|
||
func (vt *H265) WriteAVCC(ts uint32, frame AVCCFrame) {
|
||
if len(frame) < 6 {
|
||
vt.Stream.Error("AVCC data too short", zap.ByteString("data", frame))
|
||
return
|
||
}
|
||
if frame.IsSequence() {
|
||
vt.Video.dcChanged = true
|
||
vt.Video.DecoderConfiguration.Seq++
|
||
vt.Video.DecoderConfiguration.AVCC = net.Buffers{frame}
|
||
if vps, sps, pps, err := codec.ParseVpsSpsPpsFromSeqHeaderWithoutMalloc(frame); err == nil {
|
||
vt.Video.SPSInfo, _ = codec.ParseHevcSPS(frame)
|
||
vt.Video.nalulenSize = (int(frame[26]) & 0x03) + 1
|
||
vt.Video.DecoderConfiguration.Raw[0] = vps
|
||
vt.Video.DecoderConfiguration.Raw[1] = sps
|
||
vt.Video.DecoderConfiguration.Raw[2] = pps
|
||
} else {
|
||
vt.Stream.Error("H265 ParseVpsSpsPps Error")
|
||
vt.Stream.Close()
|
||
}
|
||
} else {
|
||
vt.Video.WriteAVCC(ts, frame)
|
||
vt.Video.Media.RingBuffer.Value.IFrame = frame.IsIDR()
|
||
vt.Flush()
|
||
}
|
||
}
|
||
|
||
func (vt *H265) writeRTPFrame(frame *RTPFrame) {
|
||
rv := &vt.Video.Media.RingBuffer.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.WriteSlice(NALUSlice{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) {
|
||
rv.AppendRaw(NALUSlice{[]byte{first3[0]&0b10000001 | (naluType << 1), first3[1]}})
|
||
}
|
||
lastIndex := len(rv.Raw) - 1
|
||
if lastIndex == -1 {
|
||
return
|
||
}
|
||
rv.Raw[lastIndex].Append(buffer)
|
||
if util.Bit1(fuHeader, 1) {
|
||
complete := rv.Raw[lastIndex] //拼接完成
|
||
rv.Raw = rv.Raw[:lastIndex] // 缩短一个元素,因为后面的方法会加回去
|
||
vt.WriteSlice(complete)
|
||
}
|
||
default:
|
||
vt.WriteSlice(NALUSlice{frame.Payload})
|
||
}
|
||
frame.SequenceNumber += vt.rtpSequence //增加偏移,需要增加rtp包后需要顺延
|
||
rv.AppendRTP(frame)
|
||
if frame.Marker {
|
||
vt.Video.generateTimestamp(frame.Timestamp)
|
||
vt.Flush()
|
||
}
|
||
}
|
||
func (vt *H265) Flush() {
|
||
if vt.Video.Media.RingBuffer.Value.IFrame {
|
||
vt.Video.ComputeGOP()
|
||
}
|
||
if vt.Attached == 0 && vt.IDRing != nil && vt.DecoderConfiguration.Seq > 0 {
|
||
defer vt.Attach()
|
||
}
|
||
// RTP格式补完
|
||
if config.Global.EnableRTP {
|
||
if len(vt.Value.RTP) > 0 {
|
||
if !vt.dcChanged && vt.Value.IFrame {
|
||
vt.insertDCRtp()
|
||
}
|
||
} else {
|
||
// H265打包: https://blog.csdn.net/fanyun_01/article/details/114234290
|
||
var out [][][]byte
|
||
if vt.Value.IFrame {
|
||
out = append(out, [][]byte{vt.DecoderConfiguration.Raw[0]}, [][]byte{vt.DecoderConfiguration.Raw[1]}, [][]byte{vt.DecoderConfiguration.Raw[2]})
|
||
}
|
||
for _, nalu := range vt.Video.Media.RingBuffer.Value.Raw {
|
||
buffers := util.SplitBuffers(nalu, 1200)
|
||
firstBuffer := NALUSlice(buffers[0])
|
||
if l := len(buffers); l == 1 {
|
||
out = append(out, firstBuffer)
|
||
} else {
|
||
naluType := firstBuffer.H265Type()
|
||
firstByte := (byte(codec.NAL_UNIT_RTP_FU) << 1) | (firstBuffer[0][0] & 0b10000001)
|
||
buf := [][]byte{{firstByte, firstBuffer[0][1], (1 << 7) | byte(naluType)}}
|
||
for i, sp := range firstBuffer {
|
||
if i == 0 {
|
||
sp = sp[2:]
|
||
}
|
||
buf = append(buf, sp)
|
||
}
|
||
out = append(out, buf)
|
||
for _, bufs := range buffers[1:] {
|
||
buf = append([][]byte{{firstByte, firstBuffer[0][1], byte(naluType)}}, bufs...)
|
||
out = append(out, buf)
|
||
}
|
||
buf[0][2] |= 1 << 6 // set end bit
|
||
}
|
||
}
|
||
vt.PacketizeRTP(out...)
|
||
}
|
||
}
|
||
vt.Video.Flush()
|
||
}
|