// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>// SPDX-License-Identifier: MITpackage codecsimport ()// H264Payloader payloads H264 packets.typeH264Payloaderstruct { spsNalu, ppsNalu []byte DisableStapA bool}const ( stapaNALUType = 24 fuaNALUType = 28 fubNALUType = 29 spsNALUType = 7 ppsNALUType = 8 audNALUType = 9 fillerNALUType = 12 fuaHeaderSize = 2 stapaHeaderSize = 1 stapaNALULengthSize = 2 naluTypeBitmask = 0x1F naluRefIdcBitmask = 0x60 fuStartBitmask = 0x80 fuEndBitmask = 0x40 outputStapAHeader = 0x78)// nolint:gochecknoglobalsvar ( naluStartCode = []byte{0x00, 0x00, 0x01} annexbNALUStartCode = []byte{0x00, 0x00, 0x00, 0x01})func emitNalus( []byte, func([]byte)) {// look for 3-byte NALU start code := bytes.Index(, naluStartCode) := 3if == -1 {// no start code, emit the whole buffer ()return } := len()for < {// look for the next NALU start (end of this NALU) := bytes.Index([+:], naluStartCode)if == -1 {// no more NALUs, emit the rest of the buffer ([+:])break }// next NALU start := + + // check if the next NALU is actually a 4-byte start code := [-1] == 0if { -- } ([+ : ]) = if { = 4 } else { = 3 } }}// Payload fragments a H264 packet across one or more byte arrays.func ( *H264Payloader) ( uint16, []byte) [][]byte { //nolint:cyclopvar [][]byteiflen() == 0 {return }emitNalus(, func( []byte) {iflen() == 0 {return } := [0] & naluTypeBitmask := [0] & naluRefIdcBitmaskswitch {case == audNALUType || == fillerNALUType:returncase == spsNALUType:if !.DisableStapA { .spsNalu = return }case == ppsNALUType:if !.DisableStapA { .ppsNalu = return }case !.DisableStapA && .spsNalu != nil && .ppsNalu != nil:// Pack current NALU with SPS and PPS as STAP-A := make([]byte, 2)binary.BigEndian.PutUint16(, uint16(len(.spsNalu))) // nolint: gosec // G115 := make([]byte, 2)binary.BigEndian.PutUint16(, uint16(len(.ppsNalu))) // nolint: gosec // G115 := []byte{outputStapAHeader} = append(, ...) = append(, .spsNalu...) = append(, ...) = append(, .ppsNalu...)iflen() <= int() { := make([]byte, len())copy(, ) = append(, ) } .spsNalu = nil .ppsNalu = nil }// Single NALUiflen() <= int() { := make([]byte, len())copy(, ) = append(, )return }// FU-A := int() - fuaHeaderSize// The FU payload consists of fragments of the payload of the fragmented // NAL unit so that if the fragmentation unit payloads of consecutive // FUs are sequentially concatenated, the payload of the fragmented NAL // unit can be reconstructed. The NAL unit type octet of the fragmented // NAL unit is not included as such in the fragmentation unit payload, // but rather the information of the NAL unit type octet of the // fragmented NAL unit is conveyed in the F and NRI fields of the FU // indicator octet of the fragmentation unit and in the type field of // the FU header. An FU payload MAY have any number of octets and MAY // be empty.// According to the RFC, the first octet is skipped due to redundant information := 1 := len() - := ifminInt(, ) <= 0 {return }for > 0 { := minInt(, ) := make([]byte, fuaHeaderSize+)// +---------------+ // |0|1|2|3|4|5|6|7| // +-+-+-+-+-+-+-+-+ // |F|NRI| Type | // +---------------+ [0] = fuaNALUType [0] |= // +---------------+ // |0|1|2|3|4|5|6|7| // +-+-+-+-+-+-+-+-+ // |S|E|R| Type | // +---------------+ [1] = if == {// Set start bit [1] |= 1 << 7 } elseif - == 0 {// Set end bit [1] |= 1 << 6 }copy([fuaHeaderSize:], [:+]) = append(, ) -= += } })return}// H264Packet represents the H264 header that is stored in the payload of an RTP Packet.typeH264Packetstruct { IsAVC bool fuaBuffer []bytevideoDepacketizer}func ( *H264Packet) (, []byte) []byte {if .IsAVC { = binary.BigEndian.AppendUint32(, uint32(len())) // nolint: gosec // G115 false positive = append(, ...)return } = append(, annexbNALUStartCode...) = append(, ...)return}// IsDetectedFinalPacketInSequence returns true of the packet passed in has the// marker bit set indicated the end of a packet sequence.func ( *H264Packet) ( bool) bool {return}// Unmarshal parses the passed byte slice and stores the result in the H264Packet this method is called upon.func ( *H264Packet) ( []byte) ([]byte, error) {if .zeroAllocation {return , nil }return .parseBody()}func ( *H264Packet) ( []byte) ([]byte, error) { //nolint:cyclopiflen() == 0 {returnnil, fmt.Errorf("%w: %d <=0", errShortPacket, len()) }// NALU Types // https://tools.ietf.org/html/rfc6184#section-5.4 := [0] & naluTypeBitmaskswitch {case > 0 && < 24:return .doPackaging(nil, ), nilcase == stapaNALUType: := int(stapaHeaderSize) := []byte{}for < len() { := [:]iflen() < stapaNALULengthSize {break } := int(binary.BigEndian.Uint16()) += stapaNALULengthSizeiflen() < + {returnnil, fmt.Errorf("%w STAP-A declared size(%d) is larger than buffer(%d)",errShortPacket, ,len()-, ) } = .doPackaging(, [:+]) += }return , nilcase == fuaNALUType:iflen() < fuaHeaderSize {returnnil, errShortPacket }if .fuaBuffer == nil { .fuaBuffer = []byte{} } .fuaBuffer = append(.fuaBuffer, [fuaHeaderSize:]...)if [1]&fuEndBitmask != 0 { := [0] & naluRefIdcBitmask := [1] & naluTypeBitmask := append([]byte{}, |) = append(, .fuaBuffer...) .fuaBuffer = nilreturn .doPackaging(nil, ), nil }return []byte{}, nil }returnnil, fmt.Errorf("%w: %d", errUnhandledNALUType, )}// H264PartitionHeadChecker checks H264 partition head.//// Deprecated: replaced by H264Packet.IsPartitionHead().typeH264PartitionHeadCheckerstruct{}// IsPartitionHead checks if this is the head of a packetized nalu stream.//// Deprecated: replaced by H264Packet.IsPartitionHead().func (*H264PartitionHeadChecker) ( []byte) bool {return (&H264Packet{}).IsPartitionHead()}// IsPartitionHead checks if this is the head of a packetized nalu stream.func (*H264Packet) ( []byte) bool {iflen() < 2 {returnfalse }if [0]&naluTypeBitmask == fuaNALUType || [0]&naluTypeBitmask == fubNALUType {return [1]&fuStartBitmask != 0 }returntrue}
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.