// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>// SPDX-License-Identifier: MITpackage codecsimport ()//// Errors//var ( errH265CorruptedPacket = errors.New("corrupted h265 packet") errInvalidH265PacketType = errors.New("invalid h265 packet type"))//// Network Abstraction Unit Header implementation//const (// sizeof(uint16). h265NaluHeaderSize = 2// https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.2 h265NaluAggregationPacketType = 48// https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.3 h265NaluFragmentationUnitType = 49// https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.4 h265NaluPACIPacketType = 50)// H265NALUHeader is a H265 NAL Unit Header.// https://datatracker.ietf.org/doc/html/rfc7798#section-1.1.4/** +---------------+---------------+* |0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7|* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+* |F| Type | LayerID | TID |* +-------------+-----------------+**/// .typeH265NALUHeaderuint16func newH265NALUHeader(, uint8) H265NALUHeader {returnH265NALUHeader((uint16() << 8) | uint16())}// F is the forbidden bit, should always be 0.func ( H265NALUHeader) () bool {return (uint16() >> 15) != 0}// Type of NAL Unit.func ( H265NALUHeader) () uint8 {// 01111110 00000000const = 0b01111110 << 8returnuint8((uint16() & ) >> (8 + 1)) // nolint: gosec // G115 false positive}// IsTypeVCLUnit returns whether or not the NAL Unit type is a VCL NAL unit.func ( H265NALUHeader) () bool {// Type is coded on 6 bitsconst = 0b00100000return (.Type() & ) == 0}// LayerID should always be 0 in non-3D HEVC context.func ( H265NALUHeader) () uint8 {// 00000001 11111000const = (0b00000001 << 8) | 0b11111000returnuint8((uint16() & ) >> 3) // nolint: gosec // G115 false positive}// TID is the temporal identifier of the NAL unit +1.func ( H265NALUHeader) () uint8 {const = 0b00000111returnuint8(uint16() & ) // nolint: gosec // G115 false positive}// IsAggregationPacket returns whether or not the packet is an Aggregation packet.func ( H265NALUHeader) () bool {return .Type() == h265NaluAggregationPacketType}// IsFragmentationUnit returns whether or not the packet is a Fragmentation Unit packet.func ( H265NALUHeader) () bool {return .Type() == h265NaluFragmentationUnitType}// IsPACIPacket returns whether or not the packet is a PACI packet.func ( H265NALUHeader) () bool {return .Type() == h265NaluPACIPacketType}//// Single NAL Unit Packet implementation//// H265SingleNALUnitPacket represents a NALU packet, containing exactly one NAL unit./** 0 1 2 3* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+* | PayloadHdr | DONL (conditional) |* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+* | |* | NAL unit payload data |* | |* | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+* | :...OPTIONAL RTP padding |* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+**/// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.1typeH265SingleNALUnitPacketstruct {// payloadHeader is the header of the H265 packet. payloadHeader H265NALUHeader// donl is a 16-bit field, that may or may not be present. donl *uint16// payload of the fragmentation unit. payload []byte mightNeedDONL bool}// WithDONL can be called to specify whether or not DONL might be parsed.// DONL may need to be parsed if `sprop-max-don-diff` is greater than 0 on the RTP stream.func ( *H265SingleNALUnitPacket) ( bool) { .mightNeedDONL = }// Unmarshal parses the passed byte slice and stores the result in the H265SingleNALUnitPacket// this method is called upon.func ( *H265SingleNALUnitPacket) ( []byte) ([]byte, error) {// sizeof(headers)const = h265NaluHeaderSizeif == nil {returnnil, errNilPacket } elseiflen() <= {returnnil, fmt.Errorf("%w: %d <= %v", errShortPacket, len(), ) } := newH265NALUHeader([0], [1])if .F() {returnnil, errH265CorruptedPacket }if .IsFragmentationUnit() || .IsPACIPacket() || .IsAggregationPacket() {returnnil, errInvalidH265PacketType } = [2:]if .mightNeedDONL {// sizeof(uint16)iflen() <= 2 {returnnil, errShortPacket } := (uint16([0]) << 8) | uint16([1]) .donl = & = [2:] } .payloadHeader = .payload = returnnil, nil}// PayloadHeader returns the NALU header of the packet.func ( *H265SingleNALUnitPacket) () H265NALUHeader {return .payloadHeader}// DONL returns the DONL of the packet.func ( *H265SingleNALUnitPacket) () *uint16 {return .donl}// Payload returns the Fragmentation Unit packet payload.func ( *H265SingleNALUnitPacket) () []byte {return .payload}func ( *H265SingleNALUnitPacket) () {}//// Aggregation Packets implementation//// H265AggregationUnitFirst represent the First Aggregation Unit in an AP./** 0 1 2 3* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+* : DONL (conditional) | NALU size |* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+* | NALU size | |* +-+-+-+-+-+-+-+-+ NAL unit |* | |* | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+* | :* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+**/// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.2typeH265AggregationUnitFirststruct { donl *uint16 nalUnitSize uint16 nalUnit []byte}// DONL field, when present, specifies the value of the 16 least// significant bits of the decoding order number of the aggregated NAL// unit.func ( H265AggregationUnitFirst) () *uint16 {return .donl}// NALUSize represents the size, in bytes, of the NalUnit.func ( H265AggregationUnitFirst) () uint16 {return .nalUnitSize}// NalUnit payload.func ( H265AggregationUnitFirst) () []byte {return .nalUnit}// H265AggregationUnit represent the an Aggregation Unit in an AP, which is not the first one./** 0 1 2 3* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+* : DOND (cond) | NALU size |* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+* | |* | NAL unit |* | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+* | :* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+**/// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.2typeH265AggregationUnitstruct { dond *uint8 nalUnitSize uint16 nalUnit []byte}// DOND field plus 1 specifies the difference between// the decoding order number values of the current aggregated NAL unit// and the preceding aggregated NAL unit in the same AP.func ( H265AggregationUnit) () *uint8 {return .dond}// NALUSize represents the size, in bytes, of the NalUnit.func ( H265AggregationUnit) () uint16 {return .nalUnitSize}// NalUnit payload.func ( H265AggregationUnit) () []byte {return .nalUnit}// H265AggregationPacket represents an Aggregation packet./** 0 1 2 3* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+* | PayloadHdr (Type=48) | |* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |* | |* | two or more aggregation units |* | |* | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+* | :...OPTIONAL RTP padding |* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+**/// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.2typeH265AggregationPacketstruct { firstUnit *H265AggregationUnitFirst otherUnits []H265AggregationUnit mightNeedDONL bool}// WithDONL can be called to specify whether or not DONL might be parsed.// DONL may need to be parsed if `sprop-max-don-diff` is greater than 0 on the RTP stream.func ( *H265AggregationPacket) ( bool) { .mightNeedDONL = }// Unmarshal parses the passed byte slice and stores the result in the H265AggregationPacket this method is called upon.func ( *H265AggregationPacket) ( []byte) ([]byte, error) { //nolint:cyclop// sizeof(headers)const = h265NaluHeaderSizeif == nil {returnnil, errNilPacket } elseiflen() <= {returnnil, fmt.Errorf("%w: %d <= %v", errShortPacket, len(), ) } := newH265NALUHeader([0], [1])if .F() {returnnil, errH265CorruptedPacket }if !.IsAggregationPacket() {returnnil, errInvalidH265PacketType }// First parse the first aggregation unit = [2:] := &H265AggregationUnitFirst{}if .mightNeedDONL {iflen() < 2 {returnnil, errShortPacket } := (uint16([0]) << 8) | uint16([1]) .donl = & = [2:] }iflen() < 2 {returnnil, errShortPacket } .nalUnitSize = (uint16([0]) << 8) | uint16([1]) = [2:]iflen() < int(.nalUnitSize) {returnnil, errShortPacket } .nalUnit = [:.nalUnitSize] = [.nalUnitSize:]// Parse remaining Aggregation Unitsvar []H265AggregationUnitfor { := H265AggregationUnit{}if .mightNeedDONL {iflen() < 1 {break } := [0] .dond = & = [1:] }iflen() < 2 {break } .nalUnitSize = (uint16([0]) << 8) | uint16([1]) = [2:]iflen() < int(.nalUnitSize) {break } .nalUnit = [:.nalUnitSize] = [.nalUnitSize:] = append(, ) }// There need to be **at least** two Aggregation Units (first + another one)iflen() == 0 {returnnil, errShortPacket } .firstUnit = .otherUnits = returnnil, nil}// FirstUnit returns the first Aggregated Unit of the packet.func ( *H265AggregationPacket) () *H265AggregationUnitFirst {return .firstUnit}// OtherUnits returns the all the other Aggregated Unit of the packet (excluding the first one).func ( *H265AggregationPacket) () []H265AggregationUnit {return .otherUnits}func ( *H265AggregationPacket) () {}//// Fragmentation Unit implementation//const (// sizeof(uint8). h265FragmentationUnitHeaderSize = 1)// H265FragmentationUnitHeader is a H265 FU Header.//// +---------------+// |0|1|2|3|4|5|6|7|// +-+-+-+-+-+-+-+-+// |S|E| FuType |// +---------------+// .typeH265FragmentationUnitHeaderuint8// S represents the start of a fragmented NAL unit.func ( H265FragmentationUnitHeader) () bool {const = 0b10000000return (( & ) >> 7) != 0}// E represents the end of a fragmented NAL unit.func ( H265FragmentationUnitHeader) () bool {const = 0b01000000return (( & ) >> 6) != 0}// FuType MUST be equal to the field Type of the fragmented NAL unit.func ( H265FragmentationUnitHeader) () uint8 {const = 0b00111111returnuint8() & }// H265FragmentationUnitPacket represents a single Fragmentation Unit packet./** 0 1 2 3* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+* | PayloadHdr (Type=49) | FU header | DONL (cond) |* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|* | DONL (cond) | |* |-+-+-+-+-+-+-+-+ |* | FU payload |* | |* | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+* | :...OPTIONAL RTP padding |* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+**/// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.3typeH265FragmentationUnitPacketstruct {// payloadHeader is the header of the H265 packet. payloadHeader H265NALUHeader// fuHeader is the header of the fragmentation unit fuHeader H265FragmentationUnitHeader// donl is a 16-bit field, that may or may not be present. donl *uint16// payload of the fragmentation unit. payload []byte mightNeedDONL bool}// WithDONL can be called to specify whether or not DONL might be parsed.// DONL may need to be parsed if `sprop-max-don-diff` is greater than 0 on the RTP stream.func ( *H265FragmentationUnitPacket) ( bool) { .mightNeedDONL = }// Unmarshal parses the passed byte slice and stores the result in the H265FragmentationUnitPacket// this method is called upon.func ( *H265FragmentationUnitPacket) ( []byte) ([]byte, error) {// sizeof(headers)const = h265NaluHeaderSize + h265FragmentationUnitHeaderSizeif == nil {returnnil, errNilPacket } elseiflen() <= {returnnil, fmt.Errorf("%w: %d <= %v", errShortPacket, len(), ) } := newH265NALUHeader([0], [1])if .F() {returnnil, errH265CorruptedPacket }if !.IsFragmentationUnit() {returnnil, errInvalidH265PacketType } := H265FragmentationUnitHeader([2]) = [3:]if .S() && .mightNeedDONL {// sizeof(uint16)iflen() <= 2 {returnnil, errShortPacket } := (uint16([0]) << 8) | uint16([1]) .donl = & = [2:] } .payloadHeader = .fuHeader = .payload = returnnil, nil}// PayloadHeader returns the NALU header of the packet.func ( *H265FragmentationUnitPacket) () H265NALUHeader {return .payloadHeader}// FuHeader returns the Fragmentation Unit Header of the packet.func ( *H265FragmentationUnitPacket) () H265FragmentationUnitHeader {return .fuHeader}// DONL returns the DONL of the packet.func ( *H265FragmentationUnitPacket) () *uint16 {return .donl}// Payload returns the Fragmentation Unit packet payload.func ( *H265FragmentationUnitPacket) () []byte {return .payload}func ( *H265FragmentationUnitPacket) () {}//// PACI implementation//// H265PACIPacket represents a single H265 PACI packet./** 0 1 2 3* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+* | PayloadHdr (Type=50) |A| cType | PHSsize |F0..2|Y|* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+* | Payload Header Extension Structure (PHES) |* |=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=|* | |* | PACI payload: NAL unit |* | . . . |* | |* | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+* | :...OPTIONAL RTP padding |* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+**/// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.4typeH265PACIPacketstruct {// payloadHeader is the header of the H265 packet. payloadHeader H265NALUHeader// Field which holds value for `A`, `cType`, `PHSsize`, `F0`, `F1`, `F2` and `Y` fields. paciHeaderFields uint16// phes is a header extension, of byte length `PHSsize` phes []byte// Payload contains NAL units & optional padding payload []byte}// PayloadHeader returns the NAL Unit Header.func ( *H265PACIPacket) () H265NALUHeader {return .payloadHeader}// A copies the F bit of the PACI payload NALU.func ( *H265PACIPacket) () bool {const = 0b10000000 << 8return (.paciHeaderFields & ) != 0}// CType copies the Type field of the PACI payload NALU.func ( *H265PACIPacket) () uint8 {const = 0b01111110 << 8returnuint8((.paciHeaderFields & ) >> (8 + 1)) // nolint: gosec // G115 false positive}// PHSsize indicates the size of the PHES field.func ( *H265PACIPacket) () uint8 {const = (0b00000001 << 8) | 0b11110000returnuint8((.paciHeaderFields & ) >> 4) // nolint: gosec // G115 false positive}// F0 indicates the presence of a Temporal Scalability support extension in the PHES.func ( *H265PACIPacket) () bool {const = 0b00001000return (.paciHeaderFields & ) != 0}// F1 must be zero, reserved for future extensions.func ( *H265PACIPacket) () bool {const = 0b00000100return (.paciHeaderFields & ) != 0}// F2 must be zero, reserved for future extensions.func ( *H265PACIPacket) () bool {const = 0b00000010return (.paciHeaderFields & ) != 0}// Y must be zero, reserved for future extensions.func ( *H265PACIPacket) () bool {const = 0b00000001return (.paciHeaderFields & ) != 0}// PHES contains header extensions. Its size is indicated by PHSsize.func ( *H265PACIPacket) () []byte {return .phes}// Payload is a single NALU or NALU-like struct, not including the first two octets (header).func ( *H265PACIPacket) () []byte {return .payload}// TSCI returns the Temporal Scalability Control Information extension, if present.func ( *H265PACIPacket) () *H265TSCI {if !.F0() || .PHSsize() < 3 {returnnil } := H265TSCI((uint32(.phes[0]) << 16) | (uint32(.phes[1]) << 8) | uint32(.phes[0]))return &}// Unmarshal parses the passed byte slice and stores the result in the H265PACIPacket this method is called upon.func ( *H265PACIPacket) ( []byte) ([]byte, error) {// sizeof(headers)const = h265NaluHeaderSize + 2if == nil {returnnil, errNilPacket } elseiflen() <= {returnnil, fmt.Errorf("%w: %d <= %v", errShortPacket, len(), ) } := newH265NALUHeader([0], [1])if .F() {returnnil, errH265CorruptedPacket }if !.IsPACIPacket() {returnnil, errInvalidH265PacketType } := (uint16([2]) << 8) | uint16([3]) = [4:] .paciHeaderFields = := .PHSsize()iflen() < int()+1 { .paciHeaderFields = 0returnnil, errShortPacket } .payloadHeader = if > 0 { .phes = [:] } = [:] .payload = returnnil, nil}func ( *H265PACIPacket) () {}//// Temporal Scalability Control Information//// H265TSCI is a Temporal Scalability Control Information header extension.// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.5typeH265TSCIuint32// TL0PICIDX see RFC7798 for more details.func ( H265TSCI) () uint8 {const = 0xFFFF0000const = 0xFF00returnuint8(((( & ) >> 16) & ) >> 8) // nolint: gosec // G115 false positive}// IrapPicID see RFC7798 for more details.func ( H265TSCI) () uint8 {const = 0xFFFF0000const = 0x00FFreturnuint8((( & ) >> 16) & ) // nolint: gosec // G115 false positive}// S see RFC7798 for more details.func ( H265TSCI) () bool {const = 0xFF00const = 0b10000000return (uint8((&)>>8) & ) != 0// nolint: gosec // G115 false positive}// E see RFC7798 for more details.func ( H265TSCI) () bool {const = 0xFF00const = 0b01000000return (uint8((&)>>8) & ) != 0// nolint: gosec // G115 false positive}// RES see RFC7798 for more details.func ( H265TSCI) () uint8 {const = 0xFF00const = 0b00111111returnuint8((&)>>8) & // nolint: gosec // G115 false positive}//// H265 Packet interface//type isH265Packet interface { isH265Packet()}var ( _ isH265Packet = (*H265FragmentationUnitPacket)(nil) _ isH265Packet = (*H265PACIPacket)(nil) _ isH265Packet = (*H265SingleNALUnitPacket)(nil) _ isH265Packet = (*H265AggregationPacket)(nil))//// Packet implementation//// H265Packet represents a H265 packet, stored in the payload of an RTP packet.typeH265Packetstruct { packet isH265Packet mightNeedDONL boolvideoDepacketizer}// WithDONL can be called to specify whether or not DONL might be parsed.// DONL may need to be parsed if `sprop-max-don-diff` is greater than 0 on the RTP stream.func ( *H265Packet) ( bool) { .mightNeedDONL = }// Unmarshal parses the passed byte slice and stores the result in the H265Packet this method is called upon.func ( *H265Packet) ( []byte) ([]byte, error) { // nolint:cyclopif == nil {returnnil, errNilPacket } elseiflen() <= h265NaluHeaderSize {returnnil, fmt.Errorf("%w: %d <= %v", errShortPacket, len(), h265NaluHeaderSize) } := newH265NALUHeader([0], [1])if .F() {returnnil, errH265CorruptedPacket }switch {case .IsPACIPacket(): := &H265PACIPacket{}if , := .Unmarshal(); != nil {returnnil, } .packet = case .IsFragmentationUnit(): := &H265FragmentationUnitPacket{} .WithDONL(.mightNeedDONL)if , := .Unmarshal(); != nil {returnnil, } .packet = case .IsAggregationPacket(): := &H265AggregationPacket{} .WithDONL(.mightNeedDONL)if , := .Unmarshal(); != nil {returnnil, } .packet = default: := &H265SingleNALUnitPacket{} .WithDONL(.mightNeedDONL)if , := .Unmarshal(); != nil {returnnil, } .packet = }returnnil, nil}// Packet returns the populated packet.// Must be casted to one of:// - *H265SingleNALUnitPacket// - *H265FragmentationUnitPacket// - *H265AggregationPacket// - *H265PACIPacket// nolint:golintfunc ( *H265Packet) () isH265Packet {return .packet}// IsPartitionHead checks if this is the head of a packetized nalu stream.func (*H265Packet) ( []byte) bool {iflen() < 3 {returnfalse }ifH265NALUHeader(binary.BigEndian.Uint16([0:2])).Type() == h265NaluFragmentationUnitType {returnH265FragmentationUnitHeader([2]).S() }returntrue}// H265Payloader payloads H265 packets.typeH265Payloaderstruct { AddDONL bool SkipAggregation bool donl uint16}// Payload fragments a H265 packet across one or more byte arrays.func ( *H265Payloader) ( uint16, []byte) [][]byte { //nolint:gocognit,cyclopvar [][]byteiflen() == 0 || == 0 {return } := make([][]byte, 0) := 0 := func() {iflen() == 0 {return }iflen() == 1 { //nolint:nestif// emit this as a single NALU packet := [0]if .AddDONL { := make([]byte, len()+2)// copy the NALU header to the payload headercopy([0:h265NaluHeaderSize], [0:h265NaluHeaderSize])// copy the DONL into the headerbinary.BigEndian.PutUint16([h265NaluHeaderSize:h265NaluHeaderSize+2], .donl)// write the payloadcopy([h265NaluHeaderSize+2:], [h265NaluHeaderSize:]) .donl++ = append(, ) } else {// write the nalu directly to the payload = append(, ) } } else {// construct an aggregation packet := := make([]byte, ) := uint8(math.MaxUint8) := uint8(math.MaxUint8)for , := range { := newH265NALUHeader([0], [1]) := .LayerID() := .TID()if < { = }if < { = } }binary.BigEndian.PutUint16([0:2], (uint16(h265NaluAggregationPacketType)<<9)|(uint16()<<3)|uint16()) := 2for , := range {if .AddDONL {if == 0 {binary.BigEndian.PutUint16([:+2], .donl) += 2 } else { [] = byte( - 1) ++ } }// Since the type of mtu is uint16, len(nalu) fits in as well, so it is safe. // #nosecbinary.BigEndian.PutUint16([:+2], uint16(len())) += 2 += copy([:], ) } = append(, ) }// clear the buffered NALUs = make([][]byte, 0) = 0 } := func( []byte) int { := len() + 2// +2 is NALU size Field sizeiflen() == 1 { = len() + 4// +4 are Aggregation header + NALU size Field size }if .AddDONL {iflen() == 0 { += 2 } else { ++ } }return }emitNalus(, func( []byte) {iflen() < 2 {// NALU header is 2 bytesreturn } := len() + 2if .AddDONL { += 2 }if <= int() { //nolint:nestif// this nalu fits into a single packet, either it can be emitted as // a single nalu or appended to the previous aggregation packet := ()if + > int() { () = () } = append(, ) += if .SkipAggregation {// emit this immediately. () } } else {// if this nalu doesn't fit in the current mtu, it needs to be fragmented := h265FragmentationUnitHeaderSize + 2/* payload header size */if .AddDONL { += 2 }// then, fragment the nalu := int() - := newH265NALUHeader([0], [1])// the nalu header is omitted from the fragmentation packet payload = [h265NaluHeaderSize:]if <= 0 || len() == 0 {return }// flush any buffered aggregation packets. () := len()forlen() > 0 { := len()if > { = } := make([]byte, +)// write the payload headerbinary.BigEndian.PutUint16([0:2], uint16()) [0] = ([0] & 0b10000001) | h265NaluFragmentationUnitType<<1// write the fragment header [2] = byte(H265FragmentationUnitHeader(.Type()))iflen() == {// Set start bit [2] |= 1 << 7 } elseiflen()- == 0 {// Set end bit [2] |= 1 << 6 }if .AddDONL {// write the DONL headerbinary.BigEndian.PutUint16([3:5], .donl) .donl++// copy the fragment payloadcopy([5:], [0:]) } else {// copy the fragment payloadcopy([3:], [0:]) }// append the fragment to the payload = append(, )// advance the nalu data pointer = [:] } } }) ()return}
The pages are generated with Goldsv0.8.2. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.