| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819 |
- package codecs
- import (
- "encoding/binary"
- "errors"
- "fmt"
- )
- //
- // 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 |
- // +-------------+-----------------+
- type H265NALUHeader uint16
- func newH265NALUHeader(highByte, lowByte uint8) H265NALUHeader {
- return H265NALUHeader((uint16(highByte) << 8) | uint16(lowByte))
- }
- // F is the forbidden bit, should always be 0.
- func (h H265NALUHeader) F() bool {
- return (uint16(h) >> 15) != 0
- }
- // Type of NAL Unit.
- func (h H265NALUHeader) Type() uint8 {
- // 01111110 00000000
- const mask = 0b01111110 << 8
- return uint8((uint16(h) & mask) >> (8 + 1))
- }
- // IsTypeVCLUnit returns whether or not the NAL Unit type is a VCL NAL unit.
- func (h H265NALUHeader) IsTypeVCLUnit() bool {
- // Type is coded on 6 bits
- const msbMask = 0b00100000
- return (h.Type() & msbMask) == 0
- }
- // LayerID should always be 0 in non-3D HEVC context.
- func (h H265NALUHeader) LayerID() uint8 {
- // 00000001 11111000
- const mask = (0b00000001 << 8) | 0b11111000
- return uint8((uint16(h) & mask) >> 3)
- }
- // TID is the temporal identifier of the NAL unit +1.
- func (h H265NALUHeader) TID() uint8 {
- const mask = 0b00000111
- return uint8(uint16(h) & mask)
- }
- // IsAggregationPacket returns whether or not the packet is an Aggregation packet.
- func (h H265NALUHeader) IsAggregationPacket() bool {
- return h.Type() == h265NaluAggregationPacketType
- }
- // IsFragmentationUnit returns whether or not the packet is a Fragmentation Unit packet.
- func (h H265NALUHeader) IsFragmentationUnit() bool {
- return h.Type() == h265NaluFragmentationUnitType
- }
- // IsPACIPacket returns whether or not the packet is a PACI packet.
- func (h H265NALUHeader) IsPACIPacket() bool {
- return h.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.1
- type H265SingleNALUnitPacket struct {
- // 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 (p *H265SingleNALUnitPacket) WithDONL(value bool) {
- p.mightNeedDONL = value
- }
- // Unmarshal parses the passed byte slice and stores the result in the H265SingleNALUnitPacket this method is called upon.
- func (p *H265SingleNALUnitPacket) Unmarshal(payload []byte) ([]byte, error) {
- // sizeof(headers)
- const totalHeaderSize = h265NaluHeaderSize
- if payload == nil {
- return nil, errNilPacket
- } else if len(payload) <= totalHeaderSize {
- return nil, fmt.Errorf("%w: %d <= %v", errShortPacket, len(payload), totalHeaderSize)
- }
- payloadHeader := newH265NALUHeader(payload[0], payload[1])
- if payloadHeader.F() {
- return nil, errH265CorruptedPacket
- }
- if payloadHeader.IsFragmentationUnit() || payloadHeader.IsPACIPacket() || payloadHeader.IsAggregationPacket() {
- return nil, errInvalidH265PacketType
- }
- payload = payload[2:]
- if p.mightNeedDONL {
- // sizeof(uint16)
- if len(payload) <= 2 {
- return nil, errShortPacket
- }
- donl := (uint16(payload[0]) << 8) | uint16(payload[1])
- p.donl = &donl
- payload = payload[2:]
- }
- p.payloadHeader = payloadHeader
- p.payload = payload
- return nil, nil
- }
- // PayloadHeader returns the NALU header of the packet.
- func (p *H265SingleNALUnitPacket) PayloadHeader() H265NALUHeader {
- return p.payloadHeader
- }
- // DONL returns the DONL of the packet.
- func (p *H265SingleNALUnitPacket) DONL() *uint16 {
- return p.donl
- }
- // Payload returns the Fragmentation Unit packet payload.
- func (p *H265SingleNALUnitPacket) Payload() []byte {
- return p.payload
- }
- func (p *H265SingleNALUnitPacket) isH265Packet() {}
- //
- // 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.2
- type H265AggregationUnitFirst struct {
- 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 (u H265AggregationUnitFirst) DONL() *uint16 {
- return u.donl
- }
- // NALUSize represents the size, in bytes, of the NalUnit.
- func (u H265AggregationUnitFirst) NALUSize() uint16 {
- return u.nalUnitSize
- }
- // NalUnit payload.
- func (u H265AggregationUnitFirst) NalUnit() []byte {
- return u.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.2
- type H265AggregationUnit struct {
- 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 (u H265AggregationUnit) DOND() *uint8 {
- return u.dond
- }
- // NALUSize represents the size, in bytes, of the NalUnit.
- func (u H265AggregationUnit) NALUSize() uint16 {
- return u.nalUnitSize
- }
- // NalUnit payload.
- func (u H265AggregationUnit) NalUnit() []byte {
- return u.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.2
- type H265AggregationPacket struct {
- 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 (p *H265AggregationPacket) WithDONL(value bool) {
- p.mightNeedDONL = value
- }
- // Unmarshal parses the passed byte slice and stores the result in the H265AggregationPacket this method is called upon.
- func (p *H265AggregationPacket) Unmarshal(payload []byte) ([]byte, error) {
- // sizeof(headers)
- const totalHeaderSize = h265NaluHeaderSize
- if payload == nil {
- return nil, errNilPacket
- } else if len(payload) <= totalHeaderSize {
- return nil, fmt.Errorf("%w: %d <= %v", errShortPacket, len(payload), totalHeaderSize)
- }
- payloadHeader := newH265NALUHeader(payload[0], payload[1])
- if payloadHeader.F() {
- return nil, errH265CorruptedPacket
- }
- if !payloadHeader.IsAggregationPacket() {
- return nil, errInvalidH265PacketType
- }
- // First parse the first aggregation unit
- payload = payload[2:]
- firstUnit := &H265AggregationUnitFirst{}
- if p.mightNeedDONL {
- if len(payload) < 2 {
- return nil, errShortPacket
- }
- donl := (uint16(payload[0]) << 8) | uint16(payload[1])
- firstUnit.donl = &donl
- payload = payload[2:]
- }
- if len(payload) < 2 {
- return nil, errShortPacket
- }
- firstUnit.nalUnitSize = (uint16(payload[0]) << 8) | uint16(payload[1])
- payload = payload[2:]
- if len(payload) < int(firstUnit.nalUnitSize) {
- return nil, errShortPacket
- }
- firstUnit.nalUnit = payload[:firstUnit.nalUnitSize]
- payload = payload[firstUnit.nalUnitSize:]
- // Parse remaining Aggregation Units
- var units []H265AggregationUnit
- for {
- unit := H265AggregationUnit{}
- if p.mightNeedDONL {
- if len(payload) < 1 {
- break
- }
- dond := payload[0]
- unit.dond = &dond
- payload = payload[1:]
- }
- if len(payload) < 2 {
- break
- }
- unit.nalUnitSize = (uint16(payload[0]) << 8) | uint16(payload[1])
- payload = payload[2:]
- if len(payload) < int(unit.nalUnitSize) {
- break
- }
- unit.nalUnit = payload[:unit.nalUnitSize]
- payload = payload[unit.nalUnitSize:]
- units = append(units, unit)
- }
- // There need to be **at least** two Aggregation Units (first + another one)
- if len(units) == 0 {
- return nil, errShortPacket
- }
- p.firstUnit = firstUnit
- p.otherUnits = units
- return nil, nil
- }
- // FirstUnit returns the first Aggregated Unit of the packet.
- func (p *H265AggregationPacket) FirstUnit() *H265AggregationUnitFirst {
- return p.firstUnit
- }
- // OtherUnits returns the all the other Aggregated Unit of the packet (excluding the first one).
- func (p *H265AggregationPacket) OtherUnits() []H265AggregationUnit {
- return p.otherUnits
- }
- func (p *H265AggregationPacket) isH265Packet() {}
- //
- // 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 |
- // +---------------+
- type H265FragmentationUnitHeader uint8
- // S represents the start of a fragmented NAL unit.
- func (h H265FragmentationUnitHeader) S() bool {
- const mask = 0b10000000
- return ((h & mask) >> 7) != 0
- }
- // E represents the end of a fragmented NAL unit.
- func (h H265FragmentationUnitHeader) E() bool {
- const mask = 0b01000000
- return ((h & mask) >> 6) != 0
- }
- // FuType MUST be equal to the field Type of the fragmented NAL unit.
- func (h H265FragmentationUnitHeader) FuType() uint8 {
- const mask = 0b00111111
- return uint8(h) & mask
- }
- // 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.3
- type H265FragmentationUnitPacket struct {
- // 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 (p *H265FragmentationUnitPacket) WithDONL(value bool) {
- p.mightNeedDONL = value
- }
- // Unmarshal parses the passed byte slice and stores the result in the H265FragmentationUnitPacket this method is called upon.
- func (p *H265FragmentationUnitPacket) Unmarshal(payload []byte) ([]byte, error) {
- // sizeof(headers)
- const totalHeaderSize = h265NaluHeaderSize + h265FragmentationUnitHeaderSize
- if payload == nil {
- return nil, errNilPacket
- } else if len(payload) <= totalHeaderSize {
- return nil, fmt.Errorf("%w: %d <= %v", errShortPacket, len(payload), totalHeaderSize)
- }
- payloadHeader := newH265NALUHeader(payload[0], payload[1])
- if payloadHeader.F() {
- return nil, errH265CorruptedPacket
- }
- if !payloadHeader.IsFragmentationUnit() {
- return nil, errInvalidH265PacketType
- }
- fuHeader := H265FragmentationUnitHeader(payload[2])
- payload = payload[3:]
- if fuHeader.S() && p.mightNeedDONL {
- // sizeof(uint16)
- if len(payload) <= 2 {
- return nil, errShortPacket
- }
- donl := (uint16(payload[0]) << 8) | uint16(payload[1])
- p.donl = &donl
- payload = payload[2:]
- }
- p.payloadHeader = payloadHeader
- p.fuHeader = fuHeader
- p.payload = payload
- return nil, nil
- }
- // PayloadHeader returns the NALU header of the packet.
- func (p *H265FragmentationUnitPacket) PayloadHeader() H265NALUHeader {
- return p.payloadHeader
- }
- // FuHeader returns the Fragmentation Unit Header of the packet.
- func (p *H265FragmentationUnitPacket) FuHeader() H265FragmentationUnitHeader {
- return p.fuHeader
- }
- // DONL returns the DONL of the packet.
- func (p *H265FragmentationUnitPacket) DONL() *uint16 {
- return p.donl
- }
- // Payload returns the Fragmentation Unit packet payload.
- func (p *H265FragmentationUnitPacket) Payload() []byte {
- return p.payload
- }
- func (p *H265FragmentationUnitPacket) isH265Packet() {}
- //
- // 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.4
- type H265PACIPacket struct {
- // 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 (p *H265PACIPacket) PayloadHeader() H265NALUHeader {
- return p.payloadHeader
- }
- // A copies the F bit of the PACI payload NALU.
- func (p *H265PACIPacket) A() bool {
- const mask = 0b10000000 << 8
- return (p.paciHeaderFields & mask) != 0
- }
- // CType copies the Type field of the PACI payload NALU.
- func (p *H265PACIPacket) CType() uint8 {
- const mask = 0b01111110 << 8
- return uint8((p.paciHeaderFields & mask) >> (8 + 1))
- }
- // PHSsize indicates the size of the PHES field.
- func (p *H265PACIPacket) PHSsize() uint8 {
- const mask = (0b00000001 << 8) | 0b11110000
- return uint8((p.paciHeaderFields & mask) >> 4)
- }
- // F0 indicates the presence of a Temporal Scalability support extension in the PHES.
- func (p *H265PACIPacket) F0() bool {
- const mask = 0b00001000
- return (p.paciHeaderFields & mask) != 0
- }
- // F1 must be zero, reserved for future extensions.
- func (p *H265PACIPacket) F1() bool {
- const mask = 0b00000100
- return (p.paciHeaderFields & mask) != 0
- }
- // F2 must be zero, reserved for future extensions.
- func (p *H265PACIPacket) F2() bool {
- const mask = 0b00000010
- return (p.paciHeaderFields & mask) != 0
- }
- // Y must be zero, reserved for future extensions.
- func (p *H265PACIPacket) Y() bool {
- const mask = 0b00000001
- return (p.paciHeaderFields & mask) != 0
- }
- // PHES contains header extensions. Its size is indicated by PHSsize.
- func (p *H265PACIPacket) PHES() []byte {
- return p.phes
- }
- // Payload is a single NALU or NALU-like struct, not including the first two octets (header).
- func (p *H265PACIPacket) Payload() []byte {
- return p.payload
- }
- // TSCI returns the Temporal Scalability Control Information extension, if present.
- func (p *H265PACIPacket) TSCI() *H265TSCI {
- if !p.F0() || p.PHSsize() < 3 {
- return nil
- }
- tsci := H265TSCI((uint32(p.phes[0]) << 16) | (uint32(p.phes[1]) << 8) | uint32(p.phes[0]))
- return &tsci
- }
- // Unmarshal parses the passed byte slice and stores the result in the H265PACIPacket this method is called upon.
- func (p *H265PACIPacket) Unmarshal(payload []byte) ([]byte, error) {
- // sizeof(headers)
- const totalHeaderSize = h265NaluHeaderSize + 2
- if payload == nil {
- return nil, errNilPacket
- } else if len(payload) <= totalHeaderSize {
- return nil, fmt.Errorf("%w: %d <= %v", errShortPacket, len(payload), totalHeaderSize)
- }
- payloadHeader := newH265NALUHeader(payload[0], payload[1])
- if payloadHeader.F() {
- return nil, errH265CorruptedPacket
- }
- if !payloadHeader.IsPACIPacket() {
- return nil, errInvalidH265PacketType
- }
- paciHeaderFields := (uint16(payload[2]) << 8) | uint16(payload[3])
- payload = payload[4:]
- p.paciHeaderFields = paciHeaderFields
- headerExtensionSize := p.PHSsize()
- if len(payload) < int(headerExtensionSize)+1 {
- p.paciHeaderFields = 0
- return nil, errShortPacket
- }
- p.payloadHeader = payloadHeader
- if headerExtensionSize > 0 {
- p.phes = payload[:headerExtensionSize]
- }
- payload = payload[headerExtensionSize:]
- p.payload = payload
- return nil, nil
- }
- func (p *H265PACIPacket) isH265Packet() {}
- //
- // Temporal Scalability Control Information
- //
- // H265TSCI is a Temporal Scalability Control Information header extension.
- // Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.5
- type H265TSCI uint32
- // TL0PICIDX see RFC7798 for more details.
- func (h H265TSCI) TL0PICIDX() uint8 {
- const m1 = 0xFFFF0000
- const m2 = 0xFF00
- return uint8((((h & m1) >> 16) & m2) >> 8)
- }
- // IrapPicID see RFC7798 for more details.
- func (h H265TSCI) IrapPicID() uint8 {
- const m1 = 0xFFFF0000
- const m2 = 0x00FF
- return uint8(((h & m1) >> 16) & m2)
- }
- // S see RFC7798 for more details.
- func (h H265TSCI) S() bool {
- const m1 = 0xFF00
- const m2 = 0b10000000
- return (uint8((h&m1)>>8) & m2) != 0
- }
- // E see RFC7798 for more details.
- func (h H265TSCI) E() bool {
- const m1 = 0xFF00
- const m2 = 0b01000000
- return (uint8((h&m1)>>8) & m2) != 0
- }
- // RES see RFC7798 for more details.
- func (h H265TSCI) RES() uint8 {
- const m1 = 0xFF00
- const m2 = 0b00111111
- return uint8((h&m1)>>8) & m2
- }
- //
- // 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.
- type H265Packet struct {
- packet isH265Packet
- mightNeedDONL bool
- videoDepacketizer
- }
- // 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 (p *H265Packet) WithDONL(value bool) {
- p.mightNeedDONL = value
- }
- // Unmarshal parses the passed byte slice and stores the result in the H265Packet this method is called upon
- func (p *H265Packet) Unmarshal(payload []byte) ([]byte, error) {
- if payload == nil {
- return nil, errNilPacket
- } else if len(payload) <= h265NaluHeaderSize {
- return nil, fmt.Errorf("%w: %d <= %v", errShortPacket, len(payload), h265NaluHeaderSize)
- }
- payloadHeader := newH265NALUHeader(payload[0], payload[1])
- if payloadHeader.F() {
- return nil, errH265CorruptedPacket
- }
- switch {
- case payloadHeader.IsPACIPacket():
- decoded := &H265PACIPacket{}
- if _, err := decoded.Unmarshal(payload); err != nil {
- return nil, err
- }
- p.packet = decoded
- case payloadHeader.IsFragmentationUnit():
- decoded := &H265FragmentationUnitPacket{}
- decoded.WithDONL(p.mightNeedDONL)
- if _, err := decoded.Unmarshal(payload); err != nil {
- return nil, err
- }
- p.packet = decoded
- case payloadHeader.IsAggregationPacket():
- decoded := &H265AggregationPacket{}
- decoded.WithDONL(p.mightNeedDONL)
- if _, err := decoded.Unmarshal(payload); err != nil {
- return nil, err
- }
- p.packet = decoded
- default:
- decoded := &H265SingleNALUnitPacket{}
- decoded.WithDONL(p.mightNeedDONL)
- if _, err := decoded.Unmarshal(payload); err != nil {
- return nil, err
- }
- p.packet = decoded
- }
- return nil, nil
- }
- // Packet returns the populated packet.
- // Must be casted to one of:
- // - *H265SingleNALUnitPacket
- // - *H265FragmentationUnitPacket
- // - *H265AggregationPacket
- // - *H265PACIPacket
- // nolint:golint
- func (p *H265Packet) Packet() isH265Packet {
- return p.packet
- }
- // IsPartitionHead checks if this is the head of a packetized nalu stream.
- func (*H265Packet) IsPartitionHead(payload []byte) bool {
- if len(payload) < 3 {
- return false
- }
- if H265NALUHeader(binary.BigEndian.Uint16(payload[0:2])).Type() == h265NaluFragmentationUnitType {
- return H265FragmentationUnitHeader(payload[2]).S()
- }
- return true
- }
|