summaryrefslogtreecommitdiff
path: root/vendor/github.com/pion/rtp/codecs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/pion/rtp/codecs')
-rw-r--r--vendor/github.com/pion/rtp/codecs/codecs.go2
-rw-r--r--vendor/github.com/pion/rtp/codecs/common.go8
-rw-r--r--vendor/github.com/pion/rtp/codecs/error.go11
-rw-r--r--vendor/github.com/pion/rtp/codecs/g711_packet.go22
-rw-r--r--vendor/github.com/pion/rtp/codecs/g722_packet.go22
-rw-r--r--vendor/github.com/pion/rtp/codecs/h264_packet.go205
-rw-r--r--vendor/github.com/pion/rtp/codecs/opus_packet.go44
-rw-r--r--vendor/github.com/pion/rtp/codecs/vp8_packet.go143
-rw-r--r--vendor/github.com/pion/rtp/codecs/vp9_packet.go385
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
+}