| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 |
- package codecs
- import (
- "encoding/binary"
- "fmt"
- )
- // H264Payloader payloads H264 packets
- type H264Payloader struct {
- spsNalu, ppsNalu []byte
- }
- 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
- )
- func annexbNALUStartCode() []byte { return []byte{0x00, 0x00, 0x00, 0x01} }
- func emitNalus(nals []byte, emit func([]byte)) {
- nextInd := func(nalu []byte, start int) (indStart int, indLen int) {
- zeroCount := 0
- for i, b := range nalu[start:] {
- if b == 0 {
- zeroCount++
- continue
- } else if b == 1 {
- if zeroCount >= 2 {
- return start + i - zeroCount, zeroCount + 1
- }
- }
- zeroCount = 0
- }
- return -1, -1
- }
- nextIndStart, nextIndLen := nextInd(nals, 0)
- if nextIndStart == -1 {
- emit(nals)
- } else {
- for nextIndStart != -1 {
- prevStart := nextIndStart + nextIndLen
- nextIndStart, nextIndLen = nextInd(nals, prevStart)
- if nextIndStart != -1 {
- emit(nals[prevStart:nextIndStart])
- } else {
- // Emit until end of stream, no end indicator found
- emit(nals[prevStart:])
- }
- }
- }
- }
- // Payload fragments a H264 packet across one or more byte arrays
- func (p *H264Payloader) Payload(mtu uint16, payload []byte) [][]byte {
- var payloads [][]byte
- if len(payload) == 0 {
- return payloads
- }
- emitNalus(payload, func(nalu []byte) {
- if len(nalu) == 0 {
- return
- }
- naluType := nalu[0] & naluTypeBitmask
- naluRefIdc := nalu[0] & naluRefIdcBitmask
- switch {
- case naluType == audNALUType || naluType == fillerNALUType:
- return
- case naluType == spsNALUType:
- p.spsNalu = nalu
- return
- case naluType == ppsNALUType:
- p.ppsNalu = nalu
- return
- case p.spsNalu != nil && p.ppsNalu != nil:
- // Pack current NALU with SPS and PPS as STAP-A
- spsLen := make([]byte, 2)
- binary.BigEndian.PutUint16(spsLen, uint16(len(p.spsNalu)))
- ppsLen := make([]byte, 2)
- binary.BigEndian.PutUint16(ppsLen, uint16(len(p.ppsNalu)))
- stapANalu := []byte{outputStapAHeader}
- stapANalu = append(stapANalu, spsLen...)
- stapANalu = append(stapANalu, p.spsNalu...)
- stapANalu = append(stapANalu, ppsLen...)
- stapANalu = append(stapANalu, p.ppsNalu...)
- if len(stapANalu) <= int(mtu) {
- out := make([]byte, len(stapANalu))
- copy(out, stapANalu)
- payloads = append(payloads, out)
- }
- p.spsNalu = nil
- p.ppsNalu = nil
- }
- // Single NALU
- if len(nalu) <= int(mtu) {
- out := make([]byte, len(nalu))
- copy(out, nalu)
- payloads = append(payloads, out)
- return
- }
- // FU-A
- maxFragmentSize := int(mtu) - 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.
- naluData := nalu
- // According to the RFC, the first octet is skipped due to redundant information
- naluDataIndex := 1
- naluDataLength := len(nalu) - naluDataIndex
- naluDataRemaining := naluDataLength
- if min(maxFragmentSize, naluDataRemaining) <= 0 {
- return
- }
- for naluDataRemaining > 0 {
- currentFragmentSize := min(maxFragmentSize, naluDataRemaining)
- out := make([]byte, fuaHeaderSize+currentFragmentSize)
- // +---------------+
- // |0|1|2|3|4|5|6|7|
- // +-+-+-+-+-+-+-+-+
- // |F|NRI| Type |
- // +---------------+
- out[0] = fuaNALUType
- out[0] |= naluRefIdc
- // +---------------+
- // |0|1|2|3|4|5|6|7|
- // +-+-+-+-+-+-+-+-+
- // |S|E|R| Type |
- // +---------------+
- out[1] = naluType
- if naluDataRemaining == naluDataLength {
- // Set start bit
- out[1] |= 1 << 7
- } else if naluDataRemaining-currentFragmentSize == 0 {
- // Set end bit
- out[1] |= 1 << 6
- }
- copy(out[fuaHeaderSize:], naluData[naluDataIndex:naluDataIndex+currentFragmentSize])
- payloads = append(payloads, out)
- naluDataRemaining -= currentFragmentSize
- naluDataIndex += currentFragmentSize
- }
- })
- return payloads
- }
- // H264Packet represents the H264 header that is stored in the payload of an RTP Packet
- type H264Packet struct {
- IsAVC bool
- fuaBuffer []byte
- videoDepacketizer
- }
- func (p *H264Packet) doPackaging(nalu []byte) []byte {
- if p.IsAVC {
- naluLength := make([]byte, 4)
- binary.BigEndian.PutUint32(naluLength, uint32(len(nalu)))
- return append(naluLength, nalu...)
- }
- return append(annexbNALUStartCode(), nalu...)
- }
- // IsDetectedFinalPacketInSequence returns true of the packet passed in has the
- // marker bit set indicated the end of a packet sequence
- func (p *H264Packet) IsDetectedFinalPacketInSequence(rtpPacketMarketBit bool) bool {
- return rtpPacketMarketBit
- }
- // Unmarshal parses the passed byte slice and stores the result in the H264Packet this method is called upon
- func (p *H264Packet) Unmarshal(payload []byte) ([]byte, error) {
- if payload == nil {
- return nil, errNilPacket
- } else if len(payload) <= 2 {
- return nil, fmt.Errorf("%w: %d <= 2", errShortPacket, len(payload))
- }
- // NALU Types
- // https://tools.ietf.org/html/rfc6184#section-5.4
- naluType := payload[0] & naluTypeBitmask
- switch {
- case naluType > 0 && naluType < 24:
- return p.doPackaging(payload), nil
- case naluType == stapaNALUType:
- currOffset := int(stapaHeaderSize)
- result := []byte{}
- for currOffset < len(payload) {
- naluSize := int(binary.BigEndian.Uint16(payload[currOffset:]))
- currOffset += stapaNALULengthSize
- if len(payload) < currOffset+naluSize {
- return nil, fmt.Errorf("%w STAP-A declared size(%d) is larger than buffer(%d)", errShortPacket, naluSize, len(payload)-currOffset)
- }
- result = append(result, p.doPackaging(payload[currOffset:currOffset+naluSize])...)
- currOffset += naluSize
- }
- return result, nil
- case naluType == fuaNALUType:
- if len(payload) < fuaHeaderSize {
- return nil, errShortPacket
- }
- if p.fuaBuffer == nil {
- p.fuaBuffer = []byte{}
- }
- p.fuaBuffer = append(p.fuaBuffer, payload[fuaHeaderSize:]...)
- if payload[1]&fuEndBitmask != 0 {
- naluRefIdc := payload[0] & naluRefIdcBitmask
- fragmentedNaluType := payload[1] & naluTypeBitmask
- nalu := append([]byte{}, naluRefIdc|fragmentedNaluType)
- nalu = append(nalu, p.fuaBuffer...)
- p.fuaBuffer = nil
- return p.doPackaging(nalu), nil
- }
- return []byte{}, nil
- }
- return nil, fmt.Errorf("%w: %d", errUnhandledNALUType, naluType)
- }
- // H264PartitionHeadChecker is obsolete
- type H264PartitionHeadChecker struct{}
- // IsPartitionHead checks if this is the head of a packetized nalu stream.
- func (*H264Packet) IsPartitionHead(payload []byte) bool {
- if len(payload) < 2 {
- return false
- }
- if payload[0]&naluTypeBitmask == fuaNALUType ||
- payload[0]&naluTypeBitmask == fubNALUType {
- return payload[1]&fuStartBitmask != 0
- }
- return true
- }
|