diff options
author | kali kaneko (leap communications) <kali@leap.se> | 2021-11-29 01:46:27 +0100 |
---|---|---|
committer | kali kaneko (leap communications) <kali@leap.se> | 2021-11-29 18:14:16 +0100 |
commit | 18f52af5be3a9a0c73811706108f790d65ee9c67 (patch) | |
tree | e13cbacb47d56919caa9c44a2b45dec1497a7860 /vendor/github.com/pion/rtp/codecs | |
parent | ebcef0d57b6ecb5a40c6579f6be07182dd3033ba (diff) |
[pkg] update vendor
Diffstat (limited to 'vendor/github.com/pion/rtp/codecs')
-rw-r--r-- | vendor/github.com/pion/rtp/codecs/codecs.go | 2 | ||||
-rw-r--r-- | vendor/github.com/pion/rtp/codecs/common.go | 8 | ||||
-rw-r--r-- | vendor/github.com/pion/rtp/codecs/error.go | 11 | ||||
-rw-r--r-- | vendor/github.com/pion/rtp/codecs/g711_packet.go | 22 | ||||
-rw-r--r-- | vendor/github.com/pion/rtp/codecs/g722_packet.go | 22 | ||||
-rw-r--r-- | vendor/github.com/pion/rtp/codecs/h264_packet.go | 205 | ||||
-rw-r--r-- | vendor/github.com/pion/rtp/codecs/opus_packet.go | 44 | ||||
-rw-r--r-- | vendor/github.com/pion/rtp/codecs/vp8_packet.go | 143 | ||||
-rw-r--r-- | vendor/github.com/pion/rtp/codecs/vp9_packet.go | 385 |
9 files changed, 842 insertions, 0 deletions
diff --git a/vendor/github.com/pion/rtp/codecs/codecs.go b/vendor/github.com/pion/rtp/codecs/codecs.go new file mode 100644 index 0000000..0e07897 --- /dev/null +++ b/vendor/github.com/pion/rtp/codecs/codecs.go @@ -0,0 +1,2 @@ +// Package codecs implements codec specific RTP payloader/depayloaders +package codecs diff --git a/vendor/github.com/pion/rtp/codecs/common.go b/vendor/github.com/pion/rtp/codecs/common.go new file mode 100644 index 0000000..39336d2 --- /dev/null +++ b/vendor/github.com/pion/rtp/codecs/common.go @@ -0,0 +1,8 @@ +package codecs + +func min(a, b int) int { + if a < b { + return a + } + return b +} diff --git a/vendor/github.com/pion/rtp/codecs/error.go b/vendor/github.com/pion/rtp/codecs/error.go new file mode 100644 index 0000000..38ee907 --- /dev/null +++ b/vendor/github.com/pion/rtp/codecs/error.go @@ -0,0 +1,11 @@ +package codecs + +import "errors" + +var ( + errShortPacket = errors.New("packet is not large enough") + errNilPacket = errors.New("invalid nil packet") + errTooManyPDiff = errors.New("too many PDiff") + errTooManySpatialLayers = errors.New("too many spatial layers") + errUnhandledNALUType = errors.New("NALU Type is unhandled") +) diff --git a/vendor/github.com/pion/rtp/codecs/g711_packet.go b/vendor/github.com/pion/rtp/codecs/g711_packet.go new file mode 100644 index 0000000..a74876f --- /dev/null +++ b/vendor/github.com/pion/rtp/codecs/g711_packet.go @@ -0,0 +1,22 @@ +package codecs + +// G711Payloader payloads G711 packets +type G711Payloader struct{} + +// Payload fragments an G711 packet across one or more byte arrays +func (p *G711Payloader) Payload(mtu int, payload []byte) [][]byte { + var out [][]byte + if payload == nil || mtu <= 0 { + return out + } + + for len(payload) > mtu { + o := make([]byte, mtu) + copy(o, payload[:mtu]) + payload = payload[mtu:] + out = append(out, o) + } + o := make([]byte, len(payload)) + copy(o, payload) + return append(out, o) +} diff --git a/vendor/github.com/pion/rtp/codecs/g722_packet.go b/vendor/github.com/pion/rtp/codecs/g722_packet.go new file mode 100644 index 0000000..70c9883 --- /dev/null +++ b/vendor/github.com/pion/rtp/codecs/g722_packet.go @@ -0,0 +1,22 @@ +package codecs + +// G722Payloader payloads G722 packets +type G722Payloader struct{} + +// Payload fragments an G722 packet across one or more byte arrays +func (p *G722Payloader) Payload(mtu int, payload []byte) [][]byte { + var out [][]byte + if payload == nil || mtu <= 0 { + return out + } + + for len(payload) > mtu { + o := make([]byte, mtu) + copy(o, payload[:mtu]) + payload = payload[mtu:] + out = append(out, o) + } + o := make([]byte, len(payload)) + copy(o, payload) + return append(out, o) +} 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) +} diff --git a/vendor/github.com/pion/rtp/codecs/opus_packet.go b/vendor/github.com/pion/rtp/codecs/opus_packet.go new file mode 100644 index 0000000..504741f --- /dev/null +++ b/vendor/github.com/pion/rtp/codecs/opus_packet.go @@ -0,0 +1,44 @@ +package codecs + +// OpusPayloader payloads Opus packets +type OpusPayloader struct{} + +// Payload fragments an Opus packet across one or more byte arrays +func (p *OpusPayloader) Payload(mtu int, payload []byte) [][]byte { + if payload == nil { + return [][]byte{} + } + + out := make([]byte, len(payload)) + copy(out, payload) + return [][]byte{out} +} + +// OpusPacket represents the Opus header that is stored in the payload of an RTP Packet +type OpusPacket struct { + Payload []byte +} + +// Unmarshal parses the passed byte slice and stores the result in the OpusPacket this method is called upon +func (p *OpusPacket) Unmarshal(packet []byte) ([]byte, error) { + if packet == nil { + return nil, errNilPacket + } else if len(packet) == 0 { + return nil, errShortPacket + } + + p.Payload = packet + return packet, nil +} + +// OpusPartitionHeadChecker checks Opus partition head +type OpusPartitionHeadChecker struct{} + +// IsPartitionHead checks whether if this is a head of the Opus partition +func (*OpusPartitionHeadChecker) IsPartitionHead(packet []byte) bool { + p := &OpusPacket{} + if _, err := p.Unmarshal(packet); err != nil { + return false + } + return true +} diff --git a/vendor/github.com/pion/rtp/codecs/vp8_packet.go b/vendor/github.com/pion/rtp/codecs/vp8_packet.go new file mode 100644 index 0000000..7ade7da --- /dev/null +++ b/vendor/github.com/pion/rtp/codecs/vp8_packet.go @@ -0,0 +1,143 @@ +package codecs + +// VP8Payloader payloads VP8 packets +type VP8Payloader struct{} + +const ( + vp8HeaderSize = 1 +) + +// Payload fragments a VP8 packet across one or more byte arrays +func (p *VP8Payloader) Payload(mtu int, payload []byte) [][]byte { + /* + * https://tools.ietf.org/html/rfc7741#section-4.2 + * + * 0 1 2 3 4 5 6 7 + * +-+-+-+-+-+-+-+-+ + * |X|R|N|S|R| PID | (REQUIRED) + * +-+-+-+-+-+-+-+-+ + * X: |I|L|T|K| RSV | (OPTIONAL) + * +-+-+-+-+-+-+-+-+ + * I: |M| PictureID | (OPTIONAL) + * +-+-+-+-+-+-+-+-+ + * L: | TL0PICIDX | (OPTIONAL) + * +-+-+-+-+-+-+-+-+ + * T/K: |TID|Y| KEYIDX | (OPTIONAL) + * +-+-+-+-+-+-+-+-+ + * S: Start of VP8 partition. SHOULD be set to 1 when the first payload + * octet of the RTP packet is the beginning of a new VP8 partition, + * and MUST NOT be 1 otherwise. The S bit MUST be set to 1 for the + * first packet of each encoded frame. + */ + + maxFragmentSize := mtu - vp8HeaderSize + + payloadData := payload + payloadDataRemaining := len(payload) + + payloadDataIndex := 0 + var payloads [][]byte + + // Make sure the fragment/payload size is correct + if min(maxFragmentSize, payloadDataRemaining) <= 0 { + return payloads + } + for payloadDataRemaining > 0 { + currentFragmentSize := min(maxFragmentSize, payloadDataRemaining) + out := make([]byte, vp8HeaderSize+currentFragmentSize) + if payloadDataRemaining == len(payload) { + out[0] = 0x10 + } + + copy(out[vp8HeaderSize:], payloadData[payloadDataIndex:payloadDataIndex+currentFragmentSize]) + payloads = append(payloads, out) + + payloadDataRemaining -= currentFragmentSize + payloadDataIndex += currentFragmentSize + } + + return payloads +} + +// VP8Packet represents the VP8 header that is stored in the payload of an RTP Packet +type VP8Packet struct { + // Required Header + X uint8 /* extended controlbits present */ + N uint8 /* (non-reference frame) when set to 1 this frame can be discarded */ + S uint8 /* start of VP8 partition */ + PID uint8 /* partition index */ + + // Optional Header + I uint8 /* 1 if PictureID is present */ + L uint8 /* 1 if TL0PICIDX is present */ + T uint8 /* 1 if TID is present */ + K uint8 /* 1 if KEYIDX is present */ + PictureID uint16 /* 8 or 16 bits, picture ID */ + TL0PICIDX uint8 /* 8 bits temporal level zero index */ + + Payload []byte +} + +// Unmarshal parses the passed byte slice and stores the result in the VP8Packet this method is called upon +func (p *VP8Packet) Unmarshal(payload []byte) ([]byte, error) { + if payload == nil { + return nil, errNilPacket + } + + payloadLen := len(payload) + + if payloadLen < 4 { + return nil, errShortPacket + } + + payloadIndex := 0 + + p.X = (payload[payloadIndex] & 0x80) >> 7 + p.N = (payload[payloadIndex] & 0x20) >> 5 + p.S = (payload[payloadIndex] & 0x10) >> 4 + p.PID = payload[payloadIndex] & 0x07 + + payloadIndex++ + + if p.X == 1 { + p.I = (payload[payloadIndex] & 0x80) >> 7 + p.L = (payload[payloadIndex] & 0x40) >> 6 + p.T = (payload[payloadIndex] & 0x20) >> 5 + p.K = (payload[payloadIndex] & 0x10) >> 4 + payloadIndex++ + } + + if p.I == 1 { // PID present? + if payload[payloadIndex]&0x80 > 0 { // M == 1, PID is 16bit + payloadIndex += 2 + } else { + payloadIndex++ + } + } + + if p.L == 1 { + payloadIndex++ + } + + if p.T == 1 || p.K == 1 { + payloadIndex++ + } + + if payloadIndex >= payloadLen { + return nil, errShortPacket + } + p.Payload = payload[payloadIndex:] + return p.Payload, nil +} + +// VP8PartitionHeadChecker checks VP8 partition head +type VP8PartitionHeadChecker struct{} + +// IsPartitionHead checks whether if this is a head of the VP8 partition +func (*VP8PartitionHeadChecker) IsPartitionHead(packet []byte) bool { + p := &VP8Packet{} + if _, err := p.Unmarshal(packet); err != nil { + return false + } + return p.S == 1 +} diff --git a/vendor/github.com/pion/rtp/codecs/vp9_packet.go b/vendor/github.com/pion/rtp/codecs/vp9_packet.go new file mode 100644 index 0000000..5cb619b --- /dev/null +++ b/vendor/github.com/pion/rtp/codecs/vp9_packet.go @@ -0,0 +1,385 @@ +package codecs + +import ( + "github.com/pion/randutil" +) + +// Use global random generator to properly seed by crypto grade random. +var globalMathRandomGenerator = randutil.NewMathRandomGenerator() // nolint:gochecknoglobals + +// VP9Payloader payloads VP9 packets +type VP9Payloader struct { + pictureID uint16 + initialized bool + + // InitialPictureIDFn is a function that returns random initial picture ID. + InitialPictureIDFn func() uint16 +} + +const ( + vp9HeaderSize = 3 // Flexible mode 15 bit picture ID + maxSpatialLayers = 5 + maxVP9RefPics = 3 +) + +// Payload fragments an VP9 packet across one or more byte arrays +func (p *VP9Payloader) Payload(mtu int, payload []byte) [][]byte { + /* + * https://www.ietf.org/id/draft-ietf-payload-vp9-10.txt + * + * Flexible mode (F=1) + * 0 1 2 3 4 5 6 7 + * +-+-+-+-+-+-+-+-+ + * |I|P|L|F|B|E|V|-| (REQUIRED) + * +-+-+-+-+-+-+-+-+ + * I: |M| PICTURE ID | (REQUIRED) + * +-+-+-+-+-+-+-+-+ + * M: | EXTENDED PID | (RECOMMENDED) + * +-+-+-+-+-+-+-+-+ + * L: | TID |U| SID |D| (CONDITIONALLY RECOMMENDED) + * +-+-+-+-+-+-+-+-+ -\ + * P,F: | P_DIFF |N| (CONDITIONALLY REQUIRED) - up to 3 times + * +-+-+-+-+-+-+-+-+ -/ + * V: | SS | + * | .. | + * +-+-+-+-+-+-+-+-+ + * + * Non-flexible mode (F=0) + * 0 1 2 3 4 5 6 7 + * +-+-+-+-+-+-+-+-+ + * |I|P|L|F|B|E|V|-| (REQUIRED) + * +-+-+-+-+-+-+-+-+ + * I: |M| PICTURE ID | (RECOMMENDED) + * +-+-+-+-+-+-+-+-+ + * M: | EXTENDED PID | (RECOMMENDED) + * +-+-+-+-+-+-+-+-+ + * L: | TID |U| SID |D| (CONDITIONALLY RECOMMENDED) + * +-+-+-+-+-+-+-+-+ + * | TL0PICIDX | (CONDITIONALLY REQUIRED) + * +-+-+-+-+-+-+-+-+ + * V: | SS | + * | .. | + * +-+-+-+-+-+-+-+-+ + */ + + if !p.initialized { + if p.InitialPictureIDFn == nil { + p.InitialPictureIDFn = func() uint16 { + return uint16(globalMathRandomGenerator.Intn(0x7FFF)) + } + } + p.pictureID = p.InitialPictureIDFn() & 0x7FFF + p.initialized = true + } + if payload == nil { + return [][]byte{} + } + + maxFragmentSize := mtu - vp9HeaderSize + payloadDataRemaining := len(payload) + payloadDataIndex := 0 + + if min(maxFragmentSize, payloadDataRemaining) <= 0 { + return [][]byte{} + } + + var payloads [][]byte + for payloadDataRemaining > 0 { + currentFragmentSize := min(maxFragmentSize, payloadDataRemaining) + out := make([]byte, vp9HeaderSize+currentFragmentSize) + + out[0] = 0x90 // F=1 I=1 + if payloadDataIndex == 0 { + out[0] |= 0x08 // B=1 + } + if payloadDataRemaining == currentFragmentSize { + out[0] |= 0x04 // E=1 + } + out[1] = byte(p.pictureID>>8) | 0x80 + out[2] = byte(p.pictureID) + copy(out[vp9HeaderSize:], payload[payloadDataIndex:payloadDataIndex+currentFragmentSize]) + payloads = append(payloads, out) + + payloadDataRemaining -= currentFragmentSize + payloadDataIndex += currentFragmentSize + } + p.pictureID++ + if p.pictureID >= 0x8000 { + p.pictureID = 0 + } + + return payloads +} + +// VP9Packet represents the VP9 header that is stored in the payload of an RTP Packet +type VP9Packet struct { + // Required header + I bool // PictureID is present + P bool // Inter-picture predicted frame + L bool // Layer indices is present + F bool // Flexible mode + B bool // Start of a frame + E bool // End of a frame + V bool // Scalability structure (SS) data present + + // Recommended headers + PictureID uint16 // 7 or 16 bits, picture ID + + // Conditionally recommended headers + TID uint8 // Temporal layer ID + U bool // Switching up point + SID uint8 // Spatial layer ID + D bool // Inter-layer dependency used + + // Conditionally required headers + PDiff []uint8 // Reference index (F=1) + TL0PICIDX uint8 // Temporal layer zero index (F=0) + + // Scalability structure headers + NS uint8 // N_S + 1 indicates the number of spatial layers present in the VP9 stream + Y bool // Each spatial layer's frame resolution present + G bool // PG description present flag. + NG uint8 // N_G indicates the number of pictures in a Picture Group (PG) + Width []uint16 + Height []uint16 + PGTID []uint8 // Temporal layer ID of pictures in a Picture Group + PGU []bool // Switching up point of pictures in a Picture Group + PGPDiff [][]uint8 // Reference indecies of pictures in a Picture Group + + Payload []byte +} + +// Unmarshal parses the passed byte slice and stores the result in the VP9Packet this method is called upon +func (p *VP9Packet) Unmarshal(packet []byte) ([]byte, error) { + if packet == nil { + return nil, errNilPacket + } + if len(packet) < 1 { + return nil, errShortPacket + } + + p.I = packet[0]&0x80 != 0 + p.P = packet[0]&0x40 != 0 + p.L = packet[0]&0x20 != 0 + p.F = packet[0]&0x10 != 0 + p.B = packet[0]&0x08 != 0 + p.E = packet[0]&0x04 != 0 + p.V = packet[0]&0x02 != 0 + + pos := 1 + var err error + + if p.I { + pos, err = p.parsePictureID(packet, pos) + if err != nil { + return nil, err + } + } + + if p.L { + pos, err = p.parseLayerInfo(packet, pos) + if err != nil { + return nil, err + } + } + + if p.F && p.P { + pos, err = p.parseRefIndices(packet, pos) + if err != nil { + return nil, err + } + } + + if p.V { + pos, err = p.parseSSData(packet, pos) + if err != nil { + return nil, err + } + } + + p.Payload = packet[pos:] + return p.Payload, nil +} + +// Picture ID: +// +// +-+-+-+-+-+-+-+-+ +// I: |M| PICTURE ID | M:0 => picture id is 7 bits. +// +-+-+-+-+-+-+-+-+ M:1 => picture id is 15 bits. +// M: | EXTENDED PID | +// +-+-+-+-+-+-+-+-+ +// +func (p *VP9Packet) parsePictureID(packet []byte, pos int) (int, error) { + if len(packet) <= pos { + return pos, errShortPacket + } + + p.PictureID = uint16(packet[pos] & 0x7F) + if packet[pos]&0x80 != 0 { + pos++ + if len(packet) <= pos { + return pos, errShortPacket + } + p.PictureID = p.PictureID<<8 | uint16(packet[pos]) + } + pos++ + return pos, nil +} + +func (p *VP9Packet) parseLayerInfo(packet []byte, pos int) (int, error) { + pos, err := p.parseLayerInfoCommon(packet, pos) + if err != nil { + return pos, err + } + + if p.F { + return pos, nil + } + + return p.parseLayerInfoNonFlexibleMode(packet, pos) +} + +// Layer indices (flexible mode): +// +// +-+-+-+-+-+-+-+-+ +// L: | T |U| S |D| +// +-+-+-+-+-+-+-+-+ +// +func (p *VP9Packet) parseLayerInfoCommon(packet []byte, pos int) (int, error) { + if len(packet) <= pos { + return pos, errShortPacket + } + + p.TID = packet[pos] >> 5 + p.U = packet[pos]&0x10 != 0 + p.SID = (packet[pos] >> 1) & 0x7 + p.D = packet[pos]&0x01 != 0 + + if p.SID >= maxSpatialLayers { + return pos, errTooManySpatialLayers + } + + pos++ + return pos, nil +} + +// Layer indices (non-flexible mode): +// +// +-+-+-+-+-+-+-+-+ +// L: | T |U| S |D| +// +-+-+-+-+-+-+-+-+ +// | TL0PICIDX | +// +-+-+-+-+-+-+-+-+ +// +func (p *VP9Packet) parseLayerInfoNonFlexibleMode(packet []byte, pos int) (int, error) { + if len(packet) <= pos { + return pos, errShortPacket + } + + p.TL0PICIDX = packet[pos] + pos++ + return pos, nil +} + +// Reference indices: +// +// +-+-+-+-+-+-+-+-+ P=1,F=1: At least one reference index +// P,F: | P_DIFF |N| up to 3 times has to be specified. +// +-+-+-+-+-+-+-+-+ N=1: An additional P_DIFF follows +// current P_DIFF. +// +func (p *VP9Packet) parseRefIndices(packet []byte, pos int) (int, error) { + for { + if len(packet) <= pos { + return pos, errShortPacket + } + p.PDiff = append(p.PDiff, packet[pos]>>1) + if packet[pos]&0x01 == 0 { + break + } + if len(p.PDiff) >= maxVP9RefPics { + return pos, errTooManyPDiff + } + pos++ + } + pos++ + + return pos, nil +} + +// Scalability structure (SS): +// +// +-+-+-+-+-+-+-+-+ +// V: | N_S |Y|G|-|-|-| +// +-+-+-+-+-+-+-+-+ -| +// Y: | WIDTH | (OPTIONAL) . +// + + . +// | | (OPTIONAL) . +// +-+-+-+-+-+-+-+-+ . N_S + 1 times +// | HEIGHT | (OPTIONAL) . +// + + . +// | | (OPTIONAL) . +// +-+-+-+-+-+-+-+-+ -| +// G: | N_G | (OPTIONAL) +// +-+-+-+-+-+-+-+-+ -| +// N_G: | T |U| R |-|-| (OPTIONAL) . +// +-+-+-+-+-+-+-+-+ -| . N_G times +// | P_DIFF | (OPTIONAL) . R times . +// +-+-+-+-+-+-+-+-+ -| -| +// +func (p *VP9Packet) parseSSData(packet []byte, pos int) (int, error) { + if len(packet) <= pos { + return pos, errShortPacket + } + + p.NS = packet[pos] >> 5 + p.Y = packet[pos]&0x10 != 0 + p.G = (packet[pos]>>1)&0x7 != 0 + pos++ + + NS := p.NS + 1 + p.NG = 0 + + if p.Y { + p.Width = make([]uint16, NS) + p.Height = make([]uint16, NS) + for i := 0; i < int(NS); i++ { + p.Width[i] = uint16(packet[pos])<<8 | uint16(packet[pos+1]) + pos += 2 + p.Height[i] = uint16(packet[pos])<<8 | uint16(packet[pos+1]) + pos += 2 + } + } + + if p.G { + p.NG = packet[pos] + pos++ + } + + for i := 0; i < int(p.NG); i++ { + p.PGTID = append(p.PGTID, packet[pos]>>5) + p.PGU = append(p.PGU, packet[pos]&0x10 != 0) + R := (packet[pos] >> 2) & 0x3 + pos++ + + p.PGPDiff = append(p.PGPDiff, []uint8{}) + for j := 0; j < int(R); j++ { + p.PGPDiff[i] = append(p.PGPDiff[i], packet[pos]) + pos++ + } + } + + return pos, nil +} + +// VP9PartitionHeadChecker checks VP9 partition head +type VP9PartitionHeadChecker struct{} + +// IsPartitionHead checks whether if this is a head of the VP9 partition +func (*VP9PartitionHeadChecker) IsPartitionHead(packet []byte) bool { + p := &VP9Packet{} + if _, err := p.Unmarshal(packet); err != nil { + return false + } + return p.B +} |