123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- package h264
- import "encoding/binary"
- // Payloader payloads H264 packets
- type Payloader struct {
- IsAVC bool
- stapANalu []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, isAVC bool, emit func([]byte)) {
- if !isAVC {
- 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:])
- }
- }
- }
- } else {
- for {
- n := uint32(len(nals))
- if n < 4 {
- break
- }
- end := 4 + binary.BigEndian.Uint32(nals)
- if n < end {
- break
- }
- emit(nals[4:end])
- nals = nals[end:]
- }
- }
- }
- // Payload fragments a H264 packet across one or more byte arrays
- func (p *Payloader) Payload(mtu uint16, payload []byte) [][]byte {
- var payloads [][]byte
- if len(payload) == 0 {
- return payloads
- }
- EmitNalus(payload, p.IsAVC, func(nalu []byte) {
- if len(nalu) == 0 {
- return
- }
- naluType := nalu[0] & naluTypeBitmask
- naluRefIdc := nalu[0] & naluRefIdcBitmask
- switch naluType {
- case audNALUType, fillerNALUType:
- return
- case spsNALUType, ppsNALUType:
- if p.stapANalu == nil {
- p.stapANalu = []byte{outputStapAHeader}
- }
- p.stapANalu = append(p.stapANalu, byte(len(nalu)>>8), byte(len(nalu)))
- p.stapANalu = append(p.stapANalu, nalu...)
- return
- }
- if p.stapANalu != nil {
- // Pack current NALU with SPS and PPS as STAP-A
- // Supports multiple PPS in a row
- if len(p.stapANalu) <= int(mtu) {
- payloads = append(payloads, p.stapANalu)
- }
- p.stapANalu = 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
- }
- func min(a, b int) int {
- if a < b {
- return a
- }
- return b
- }
|