diff options
Diffstat (limited to 'vendor/github.com/pion/rtp/codecs/h264_packet.go')
-rw-r--r-- | vendor/github.com/pion/rtp/codecs/h264_packet.go | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/vendor/github.com/pion/rtp/codecs/h264_packet.go b/vendor/github.com/pion/rtp/codecs/h264_packet.go new file mode 100644 index 0000000..3ee5926 --- /dev/null +++ b/vendor/github.com/pion/rtp/codecs/h264_packet.go @@ -0,0 +1,205 @@ +package codecs + +import ( + "encoding/binary" + "fmt" +) + +// H264Payloader payloads H264 packets +type H264Payloader struct{} + +const ( + stapaNALUType = 24 + fuaNALUType = 28 + + fuaHeaderSize = 2 + stapaHeaderSize = 1 + stapaNALULengthSize = 2 + + naluTypeBitmask = 0x1F + naluRefIdcBitmask = 0x60 + fuaStartBitmask = 0x80 +) + +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 int, 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 + + if naluType == 9 || naluType == 12 { + return + } + + // Single NALU + if len(nalu) <= mtu { + out := make([]byte, len(nalu)) + copy(out, nalu) + payloads = append(payloads, out) + return + } + + // FU-A + maxFragmentSize := 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 { +} + +// 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 append(annexbNALUStartCode(), 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, annexbNALUStartCode()...) + result = append(result, payload[currOffset:currOffset+naluSize]...) + currOffset += naluSize + } + return result, nil + + case naluType == fuaNALUType: + if len(payload) < fuaHeaderSize { + return nil, errShortPacket + } + + if payload[1]&fuaStartBitmask != 0 { + naluRefIdc := payload[0] & naluRefIdcBitmask + fragmentedNaluType := payload[1] & naluTypeBitmask + + // Take a copy of payload since we are mutating it. + payloadCopy := append([]byte{}, payload...) + payloadCopy[fuaHeaderSize-1] = naluRefIdc | fragmentedNaluType + return append(annexbNALUStartCode(), payloadCopy[fuaHeaderSize-1:]...), nil + } + + return payload[fuaHeaderSize:], nil + } + + return nil, fmt.Errorf("%w: %d", errUnhandledNALUType, naluType) +} |