mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
For regression test, add srs-bench to 3rdparty
This commit is contained in:
parent
de87dd427d
commit
876210f6c9
1158 changed files with 256967 additions and 3 deletions
2
trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/codecs.go
generated
vendored
Normal file
2
trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/codecs.go
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
// Package codecs implements codec specific RTP payloader/depayloaders
|
||||
package codecs
|
8
trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/common.go
generated
vendored
Normal file
8
trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/common.go
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
package codecs
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
11
trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/error.go
generated
vendored
Normal file
11
trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/error.go
generated
vendored
Normal file
|
@ -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")
|
||||
)
|
22
trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/g711_packet.go
generated
vendored
Normal file
22
trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/g711_packet.go
generated
vendored
Normal file
|
@ -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)
|
||||
}
|
22
trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/g722_packet.go
generated
vendored
Normal file
22
trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/g722_packet.go
generated
vendored
Normal file
|
@ -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)
|
||||
}
|
205
trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/h264_packet.go
generated
vendored
Normal file
205
trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/h264_packet.go
generated
vendored
Normal file
|
@ -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)
|
||||
}
|
44
trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/opus_packet.go
generated
vendored
Normal file
44
trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/opus_packet.go
generated
vendored
Normal file
|
@ -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
|
||||
}
|
143
trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/vp8_packet.go
generated
vendored
Normal file
143
trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/vp8_packet.go
generated
vendored
Normal file
|
@ -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
|
||||
}
|
385
trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/vp9_packet.go
generated
vendored
Normal file
385
trunk/3rdparty/srs-bench/vendor/github.com/pion/rtp/codecs/vp9_packet.go
generated
vendored
Normal file
|
@ -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
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue