1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-03-09 15:49:59 +00:00

TEST: Upgrade pion to v3.2.9. (#3567)

------

Co-authored-by: chundonglinlin <chundonglinlin@163.com>
This commit is contained in:
Winlin 2023-06-05 11:25:04 +08:00 committed by GitHub
parent 104cf14d68
commit df854339ea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
1383 changed files with 118469 additions and 41421 deletions

View file

@ -22,3 +22,4 @@ cover.out
*.wasm
examples/sfu-ws/cert.pem
examples/sfu-ws/key.pem
wasm_exec.js

View file

@ -0,0 +1,36 @@
# Thank you to everyone that made Pion possible. If you are interested in contributing
# we would love to have you https://github.com/pion/webrtc/wiki/Contributing
#
# This file is auto generated, using git to list all individuals contributors.
# see `.github/generate-authors.sh` for the scripting
adwpc <adwpc@hotmail.com>
aler9 <46489434+aler9@users.noreply.github.com>
Antoine Baché <antoine.bache@epitech.eu>
Antoine Baché <antoine@tenten.app>
Atsushi Watanabe <atsushi.w@ieee.org>
baiyufei <baiyufei@outlook.com>
Bao Nguyen <bao@n4n.dev>
boks1971 <raja.gobi@tutanota.com>
debiandebiandebian <debiandebiandebiandebian@gmail.com>
ffmiyo <leffmiyo@gmail.com>
Guilherme <gqgs@protonmail.com>
Haiyang Wang <ocean2811@outlook.com>
Hugo Arregui <hugo@decentraland.org>
John Bradley <jrb@turrettech.com>
Juliusz Chroboczek <jch@irif.fr>
Kazuyuki Honda <hakobera@gmail.com>
Luke Curley <kixelated@gmail.com>
lxb <xiangbing.li@uama.com.cn>
Michael MacDonald <github@macdonald.cx>
Michael MacDonald <mike.macdonald@savantsystems.com>
Michael Uti <utimichael9@gmail.com>
Raphael Derosso Pereira <raphaelpereira@gmail.com>
Rob Lofthouse <ri.lofthouse@gmail.com>
Robin Raymond <robin@goheadroom.com>
Sean DuBois <duboisea@twitch.tv>
Sean DuBois <seaduboi@amazon.com>
Sean DuBois <sean@siobud.com>
Simone Gotti <simone.gotti@gmail.com>
Tarrence van As <tarrence13@gmail.com>
wangzixiang <wangzixiang@onething.net>
Woodrow Douglass <wdouglass@carnegierobotics.com>

View file

@ -30,24 +30,5 @@ If you need commercial support or don't want to use public methods you can conta
### Contributing
Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible:
* [John Bradley](https://github.com/kc5nra) - *Original Author*
* [Sean DuBois](https://github.com/Sean-Der) - *Original Author*
* [Woodrow Douglass](https://github.com/wdouglass) *RTCP, RTP improvements, G.722 support, Bugfixes*
* [Michael MacDonald](https://github.com/mjmac)
* [Luke Curley](https://github.com/kixelated) *Performance*
* [Antoine Baché](https://github.com/Antonito) *Fixed crashes*
* [Hugo Arregui](https://github.com/hugoArregui)
* [Raphael Derosso Pereira](https://github.com/raphaelpereira)
* [Atsushi Watanabe](https://github.com/at-wat)
* [adwpc](https://github.com/adwpc) *add transport-cc extension*
* [Bao Nguyen](https://github.com/sysbot) *add VP9 noop, bug fixes.
* [Tarrence van As](https://github.com/tarrencev) *add audio level extension*
* [Simone Gotti](https://github.com/sgotti)
* [Guilherme Souza](https://github.com/gqgs)
* [Rob Lofthouse](https://github.com/roblofthouse)
* [Kazuyuki Honda](https://github.com/hakobera)
* [Haiyang Wang](https://github.com/ocean2811)
* [lxb](https://github.com/lxb531)
### License
MIT License - see [LICENSE](LICENSE) for full text

View file

@ -15,7 +15,7 @@ type AbsSendTimeExtension struct {
}
// Marshal serializes the members to buffer.
func (t *AbsSendTimeExtension) Marshal() ([]byte, error) {
func (t AbsSendTimeExtension) Marshal() ([]byte, error) {
return []byte{
byte(t.Timestamp & 0xFF0000 >> 16),
byte(t.Timestamp & 0xFF00 >> 8),

View file

@ -36,7 +36,7 @@ type AudioLevelExtension struct {
}
// Marshal serializes the members to buffer
func (a *AudioLevelExtension) Marshal() ([]byte, error) {
func (a AudioLevelExtension) Marshal() ([]byte, error) {
if a.Level > 127 {
return nil, errAudioLevelOverflow
}

View file

@ -0,0 +1,158 @@
package codecs
import (
"github.com/pion/rtp/pkg/obu"
)
const (
zMask = byte(0b10000000)
zBitshift = 7
yMask = byte(0b01000000)
yBitshift = 6
wMask = byte(0b00110000)
wBitshift = 4
nMask = byte(0b00001000)
nBitshift = 3
av1PayloaderHeadersize = 1
)
// AV1Payloader payloads AV1 packets
type AV1Payloader struct{}
// Payload fragments a AV1 packet across one or more byte arrays
// See AV1Packet for description of AV1 Payload Header
func (p *AV1Payloader) Payload(mtu uint16, payload []byte) (payloads [][]byte) {
maxFragmentSize := int(mtu) - av1PayloaderHeadersize - 2
payloadDataRemaining := len(payload)
payloadDataIndex := 0
// Make sure the fragment/payload size is correct
if min(maxFragmentSize, payloadDataRemaining) <= 0 {
return payloads
}
for payloadDataRemaining > 0 {
currentFragmentSize := min(maxFragmentSize, payloadDataRemaining)
leb128Size := 1
if currentFragmentSize >= 127 {
leb128Size = 2
}
out := make([]byte, av1PayloaderHeadersize+leb128Size+currentFragmentSize)
leb128Value := obu.EncodeLEB128(uint(currentFragmentSize))
if leb128Size == 1 {
out[1] = byte(leb128Value)
} else {
out[1] = byte(leb128Value >> 8)
out[2] = byte(leb128Value)
}
copy(out[av1PayloaderHeadersize+leb128Size:], payload[payloadDataIndex:payloadDataIndex+currentFragmentSize])
payloads = append(payloads, out)
payloadDataRemaining -= currentFragmentSize
payloadDataIndex += currentFragmentSize
if len(payloads) > 1 {
out[0] ^= zMask
}
if payloadDataRemaining != 0 {
out[0] ^= yMask
}
}
return payloads
}
// AV1Packet represents a depacketized AV1 RTP Packet
//
// 0 1 2 3 4 5 6 7
// +-+-+-+-+-+-+-+-+
// |Z|Y| W |N|-|-|-|
// +-+-+-+-+-+-+-+-+
//
// https://aomediacodec.github.io/av1-rtp-spec/#44-av1-aggregation-header
type AV1Packet struct {
// Z: MUST be set to 1 if the first OBU element is an
// OBU fragment that is a continuation of an OBU fragment
// from the previous packet, and MUST be set to 0 otherwise.
Z bool
// Y: MUST be set to 1 if the last OBU element is an OBU fragment
// that will continue in the next packet, and MUST be set to 0 otherwise.
Y bool
// W: two bit field that describes the number of OBU elements in the packet.
// This field MUST be set equal to 0 or equal to the number of OBU elements
// contained in the packet. If set to 0, each OBU element MUST be preceded by
// a length field. If not set to 0 (i.e., W = 1, 2 or 3) the last OBU element
// MUST NOT be preceded by a length field. Instead, the length of the last OBU
// element contained in the packet can be calculated as follows:
// Length of the last OBU element =
// length of the RTP payload
// - length of aggregation header
// - length of previous OBU elements including length fields
W byte
// N: MUST be set to 1 if the packet is the first packet of a coded video sequence, and MUST be set to 0 otherwise.
N bool
// Each AV1 RTP Packet is a collection of OBU Elements. Each OBU Element may be a full OBU, or just a fragment of one.
// AV1Frame provides the tools to construct a collection of OBUs from a collection of OBU Elements
OBUElements [][]byte
}
// Unmarshal parses the passed byte slice and stores the result in the AV1Packet this method is called upon
func (p *AV1Packet) Unmarshal(payload []byte) ([]byte, error) {
if payload == nil {
return nil, errNilPacket
} else if len(payload) < 2 {
return nil, errShortPacket
}
p.Z = ((payload[0] & zMask) >> zBitshift) != 0
p.Y = ((payload[0] & yMask) >> yBitshift) != 0
p.N = ((payload[0] & nMask) >> nBitshift) != 0
p.W = (payload[0] & wMask) >> wBitshift
if p.Z && p.N {
return nil, errIsKeyframeAndFragment
}
currentIndex := uint(1)
p.OBUElements = [][]byte{}
var (
obuElementLength, bytesRead uint
err error
)
for i := 1; ; i++ {
if currentIndex == uint(len(payload)) {
break
}
// If W bit is set the last OBU Element will have no length header
if byte(i) == p.W {
bytesRead = 0
obuElementLength = uint(len(payload)) - currentIndex
} else {
obuElementLength, bytesRead, err = obu.ReadLeb128(payload[currentIndex:])
if err != nil {
return nil, err
}
}
currentIndex += bytesRead
if uint(len(payload)) < currentIndex+obuElementLength {
return nil, errShortPacket
}
p.OBUElements = append(p.OBUElements, payload[currentIndex:currentIndex+obuElementLength])
currentIndex += obuElementLength
}
return payload[1:], nil
}

View file

@ -6,3 +6,21 @@ func min(a, b int) int {
}
return b
}
// audioDepacketizer is a mixin for audio codec depacketizers
type audioDepacketizer struct{}
func (d *audioDepacketizer) IsPartitionTail(marker bool, payload []byte) bool {
return true
}
func (d *audioDepacketizer) IsPartitionHead(payload []byte) bool {
return true
}
// videoDepacketizer is a mixin for video codec depacketizers
type videoDepacketizer struct{}
func (d *videoDepacketizer) IsPartitionTail(marker bool, payload []byte) bool {
return marker
}

View file

@ -8,4 +8,7 @@ var (
errTooManyPDiff = errors.New("too many PDiff")
errTooManySpatialLayers = errors.New("too many spatial layers")
errUnhandledNALUType = errors.New("NALU Type is unhandled")
// AV1 Errors
errIsKeyframeAndFragment = errors.New("bits Z and N are set. Not possible to have OBU be tail fragment and be keyframe")
)

View file

@ -4,13 +4,13 @@ package codecs
type G711Payloader struct{}
// Payload fragments an G711 packet across one or more byte arrays
func (p *G711Payloader) Payload(mtu int, payload []byte) [][]byte {
func (p *G711Payloader) Payload(mtu uint16, payload []byte) [][]byte {
var out [][]byte
if payload == nil || mtu <= 0 {
if payload == nil || mtu == 0 {
return out
}
for len(payload) > mtu {
for len(payload) > int(mtu) {
o := make([]byte, mtu)
copy(o, payload[:mtu])
payload = payload[mtu:]

View file

@ -4,13 +4,13 @@ package codecs
type G722Payloader struct{}
// Payload fragments an G722 packet across one or more byte arrays
func (p *G722Payloader) Payload(mtu int, payload []byte) [][]byte {
func (p *G722Payloader) Payload(mtu uint16, payload []byte) [][]byte {
var out [][]byte
if payload == nil || mtu <= 0 {
if payload == nil || mtu == 0 {
return out
}
for len(payload) > mtu {
for len(payload) > int(mtu) {
o := make([]byte, mtu)
copy(o, payload[:mtu])
payload = payload[mtu:]

View file

@ -6,11 +6,18 @@ import (
)
// H264Payloader payloads H264 packets
type H264Payloader struct{}
type H264Payloader struct {
spsNalu, ppsNalu []byte
}
const (
stapaNALUType = 24
fuaNALUType = 28
stapaNALUType = 24
fuaNALUType = 28
fubNALUType = 29
spsNALUType = 7
ppsNALUType = 8
audNALUType = 9
fillerNALUType = 12
fuaHeaderSize = 2
stapaHeaderSize = 1
@ -18,7 +25,10 @@ const (
naluTypeBitmask = 0x1F
naluRefIdcBitmask = 0x60
fuaStartBitmask = 0x80
fuStartBitmask = 0x80
fuEndBitmask = 0x40
outputStapAHeader = 0x78
)
func annexbNALUStartCode() []byte { return []byte{0x00, 0x00, 0x00, 0x01} }
@ -59,7 +69,7 @@ func emitNalus(nals []byte, emit func([]byte)) {
}
// Payload fragments a H264 packet across one or more byte arrays
func (p *H264Payloader) Payload(mtu int, payload []byte) [][]byte {
func (p *H264Payloader) Payload(mtu uint16, payload []byte) [][]byte {
var payloads [][]byte
if len(payload) == 0 {
return payloads
@ -73,12 +83,40 @@ func (p *H264Payloader) Payload(mtu int, payload []byte) [][]byte {
naluType := nalu[0] & naluTypeBitmask
naluRefIdc := nalu[0] & naluRefIdcBitmask
if naluType == 9 || naluType == 12 {
switch {
case naluType == audNALUType || naluType == fillerNALUType:
return
case naluType == spsNALUType:
p.spsNalu = nalu
return
case naluType == ppsNALUType:
p.ppsNalu = nalu
return
case p.spsNalu != nil && p.ppsNalu != nil:
// Pack current NALU with SPS and PPS as STAP-A
spsLen := make([]byte, 2)
binary.BigEndian.PutUint16(spsLen, uint16(len(p.spsNalu)))
ppsLen := make([]byte, 2)
binary.BigEndian.PutUint16(ppsLen, uint16(len(p.ppsNalu)))
stapANalu := []byte{outputStapAHeader}
stapANalu = append(stapANalu, spsLen...)
stapANalu = append(stapANalu, p.spsNalu...)
stapANalu = append(stapANalu, ppsLen...)
stapANalu = append(stapANalu, p.ppsNalu...)
if len(stapANalu) <= int(mtu) {
out := make([]byte, len(stapANalu))
copy(out, stapANalu)
payloads = append(payloads, out)
}
p.spsNalu = nil
p.ppsNalu = nil
}
// Single NALU
if len(nalu) <= mtu {
if len(nalu) <= int(mtu) {
out := make([]byte, len(nalu))
copy(out, nalu)
payloads = append(payloads, out)
@ -86,7 +124,7 @@ func (p *H264Payloader) Payload(mtu int, payload []byte) [][]byte {
}
// FU-A
maxFragmentSize := mtu - fuaHeaderSize
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
@ -149,6 +187,26 @@ func (p *H264Payloader) Payload(mtu int, payload []byte) [][]byte {
// H264Packet represents the H264 header that is stored in the payload of an RTP Packet
type H264Packet struct {
IsAVC bool
fuaBuffer []byte
videoDepacketizer
}
func (p *H264Packet) doPackaging(nalu []byte) []byte {
if p.IsAVC {
naluLength := make([]byte, 4)
binary.BigEndian.PutUint32(naluLength, uint32(len(nalu)))
return append(naluLength, nalu...)
}
return append(annexbNALUStartCode(), nalu...)
}
// IsDetectedFinalPacketInSequence returns true of the packet passed in has the
// marker bit set indicated the end of a packet sequence
func (p *H264Packet) IsDetectedFinalPacketInSequence(rtpPacketMarketBit bool) bool {
return rtpPacketMarketBit
}
// Unmarshal parses the passed byte slice and stores the result in the H264Packet this method is called upon
@ -164,7 +222,7 @@ func (p *H264Packet) Unmarshal(payload []byte) ([]byte, error) {
naluType := payload[0] & naluTypeBitmask
switch {
case naluType > 0 && naluType < 24:
return append(annexbNALUStartCode(), payload...), nil
return p.doPackaging(payload), nil
case naluType == stapaNALUType:
currOffset := int(stapaHeaderSize)
@ -177,8 +235,7 @@ func (p *H264Packet) Unmarshal(payload []byte) ([]byte, error) {
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]...)
result = append(result, p.doPackaging(payload[currOffset:currOffset+naluSize])...)
currOffset += naluSize
}
return result, nil
@ -188,18 +245,41 @@ func (p *H264Packet) Unmarshal(payload []byte) ([]byte, error) {
return nil, errShortPacket
}
if payload[1]&fuaStartBitmask != 0 {
if p.fuaBuffer == nil {
p.fuaBuffer = []byte{}
}
p.fuaBuffer = append(p.fuaBuffer, payload[fuaHeaderSize:]...)
if payload[1]&fuEndBitmask != 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
nalu := append([]byte{}, naluRefIdc|fragmentedNaluType)
nalu = append(nalu, p.fuaBuffer...)
p.fuaBuffer = nil
return p.doPackaging(nalu), nil
}
return payload[fuaHeaderSize:], nil
return []byte{}, nil
}
return nil, fmt.Errorf("%w: %d", errUnhandledNALUType, naluType)
}
// H264PartitionHeadChecker is obsolete
type H264PartitionHeadChecker struct{}
// IsPartitionHead checks if this is the head of a packetized nalu stream.
func (*H264Packet) IsPartitionHead(payload []byte) bool {
if len(payload) < 2 {
return false
}
if payload[0]&naluTypeBitmask == fuaNALUType ||
payload[0]&naluTypeBitmask == fubNALUType {
return payload[1]&fuStartBitmask != 0
}
return true
}

View file

@ -0,0 +1,819 @@
package codecs
import (
"encoding/binary"
"errors"
"fmt"
)
//
// Errors
//
var (
errH265CorruptedPacket = errors.New("corrupted h265 packet")
errInvalidH265PacketType = errors.New("invalid h265 packet type")
)
//
// Network Abstraction Unit Header implementation
//
const (
// sizeof(uint16)
h265NaluHeaderSize = 2
// https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.2
h265NaluAggregationPacketType = 48
// https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.3
h265NaluFragmentationUnitType = 49
// https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.4
h265NaluPACIPacketType = 50
)
// H265NALUHeader is a H265 NAL Unit Header
// https://datatracker.ietf.org/doc/html/rfc7798#section-1.1.4
// +---------------+---------------+
// |0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |F| Type | LayerID | TID |
// +-------------+-----------------+
type H265NALUHeader uint16
func newH265NALUHeader(highByte, lowByte uint8) H265NALUHeader {
return H265NALUHeader((uint16(highByte) << 8) | uint16(lowByte))
}
// F is the forbidden bit, should always be 0.
func (h H265NALUHeader) F() bool {
return (uint16(h) >> 15) != 0
}
// Type of NAL Unit.
func (h H265NALUHeader) Type() uint8 {
// 01111110 00000000
const mask = 0b01111110 << 8
return uint8((uint16(h) & mask) >> (8 + 1))
}
// IsTypeVCLUnit returns whether or not the NAL Unit type is a VCL NAL unit.
func (h H265NALUHeader) IsTypeVCLUnit() bool {
// Type is coded on 6 bits
const msbMask = 0b00100000
return (h.Type() & msbMask) == 0
}
// LayerID should always be 0 in non-3D HEVC context.
func (h H265NALUHeader) LayerID() uint8 {
// 00000001 11111000
const mask = (0b00000001 << 8) | 0b11111000
return uint8((uint16(h) & mask) >> 3)
}
// TID is the temporal identifier of the NAL unit +1.
func (h H265NALUHeader) TID() uint8 {
const mask = 0b00000111
return uint8(uint16(h) & mask)
}
// IsAggregationPacket returns whether or not the packet is an Aggregation packet.
func (h H265NALUHeader) IsAggregationPacket() bool {
return h.Type() == h265NaluAggregationPacketType
}
// IsFragmentationUnit returns whether or not the packet is a Fragmentation Unit packet.
func (h H265NALUHeader) IsFragmentationUnit() bool {
return h.Type() == h265NaluFragmentationUnitType
}
// IsPACIPacket returns whether or not the packet is a PACI packet.
func (h H265NALUHeader) IsPACIPacket() bool {
return h.Type() == h265NaluPACIPacketType
}
//
// Single NAL Unit Packet implementation
//
// H265SingleNALUnitPacket represents a NALU packet, containing exactly one NAL unit.
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | PayloadHdr | DONL (conditional) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | |
// | NAL unit payload data |
// | |
// | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | :...OPTIONAL RTP padding |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.1
type H265SingleNALUnitPacket struct {
// payloadHeader is the header of the H265 packet.
payloadHeader H265NALUHeader
// donl is a 16-bit field, that may or may not be present.
donl *uint16
// payload of the fragmentation unit.
payload []byte
mightNeedDONL bool
}
// WithDONL can be called to specify whether or not DONL might be parsed.
// DONL may need to be parsed if `sprop-max-don-diff` is greater than 0 on the RTP stream.
func (p *H265SingleNALUnitPacket) WithDONL(value bool) {
p.mightNeedDONL = value
}
// Unmarshal parses the passed byte slice and stores the result in the H265SingleNALUnitPacket this method is called upon.
func (p *H265SingleNALUnitPacket) Unmarshal(payload []byte) ([]byte, error) {
// sizeof(headers)
const totalHeaderSize = h265NaluHeaderSize
if payload == nil {
return nil, errNilPacket
} else if len(payload) <= totalHeaderSize {
return nil, fmt.Errorf("%w: %d <= %v", errShortPacket, len(payload), totalHeaderSize)
}
payloadHeader := newH265NALUHeader(payload[0], payload[1])
if payloadHeader.F() {
return nil, errH265CorruptedPacket
}
if payloadHeader.IsFragmentationUnit() || payloadHeader.IsPACIPacket() || payloadHeader.IsAggregationPacket() {
return nil, errInvalidH265PacketType
}
payload = payload[2:]
if p.mightNeedDONL {
// sizeof(uint16)
if len(payload) <= 2 {
return nil, errShortPacket
}
donl := (uint16(payload[0]) << 8) | uint16(payload[1])
p.donl = &donl
payload = payload[2:]
}
p.payloadHeader = payloadHeader
p.payload = payload
return nil, nil
}
// PayloadHeader returns the NALU header of the packet.
func (p *H265SingleNALUnitPacket) PayloadHeader() H265NALUHeader {
return p.payloadHeader
}
// DONL returns the DONL of the packet.
func (p *H265SingleNALUnitPacket) DONL() *uint16 {
return p.donl
}
// Payload returns the Fragmentation Unit packet payload.
func (p *H265SingleNALUnitPacket) Payload() []byte {
return p.payload
}
func (p *H265SingleNALUnitPacket) isH265Packet() {}
//
// Aggregation Packets implementation
//
// H265AggregationUnitFirst represent the First Aggregation Unit in an AP.
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// : DONL (conditional) | NALU size |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | NALU size | |
// +-+-+-+-+-+-+-+-+ NAL unit |
// | |
// | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | :
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.2
type H265AggregationUnitFirst struct {
donl *uint16
nalUnitSize uint16
nalUnit []byte
}
// DONL field, when present, specifies the value of the 16 least
// significant bits of the decoding order number of the aggregated NAL
// unit.
func (u H265AggregationUnitFirst) DONL() *uint16 {
return u.donl
}
// NALUSize represents the size, in bytes, of the NalUnit.
func (u H265AggregationUnitFirst) NALUSize() uint16 {
return u.nalUnitSize
}
// NalUnit payload.
func (u H265AggregationUnitFirst) NalUnit() []byte {
return u.nalUnit
}
// H265AggregationUnit represent the an Aggregation Unit in an AP, which is not the first one.
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// : DOND (cond) | NALU size |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | |
// | NAL unit |
// | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | :
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.2
type H265AggregationUnit struct {
dond *uint8
nalUnitSize uint16
nalUnit []byte
}
// DOND field plus 1 specifies the difference between
// the decoding order number values of the current aggregated NAL unit
// and the preceding aggregated NAL unit in the same AP.
func (u H265AggregationUnit) DOND() *uint8 {
return u.dond
}
// NALUSize represents the size, in bytes, of the NalUnit.
func (u H265AggregationUnit) NALUSize() uint16 {
return u.nalUnitSize
}
// NalUnit payload.
func (u H265AggregationUnit) NalUnit() []byte {
return u.nalUnit
}
// H265AggregationPacket represents an Aggregation packet.
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | PayloadHdr (Type=48) | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
// | |
// | two or more aggregation units |
// | |
// | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | :...OPTIONAL RTP padding |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.2
type H265AggregationPacket struct {
firstUnit *H265AggregationUnitFirst
otherUnits []H265AggregationUnit
mightNeedDONL bool
}
// WithDONL can be called to specify whether or not DONL might be parsed.
// DONL may need to be parsed if `sprop-max-don-diff` is greater than 0 on the RTP stream.
func (p *H265AggregationPacket) WithDONL(value bool) {
p.mightNeedDONL = value
}
// Unmarshal parses the passed byte slice and stores the result in the H265AggregationPacket this method is called upon.
func (p *H265AggregationPacket) Unmarshal(payload []byte) ([]byte, error) {
// sizeof(headers)
const totalHeaderSize = h265NaluHeaderSize
if payload == nil {
return nil, errNilPacket
} else if len(payload) <= totalHeaderSize {
return nil, fmt.Errorf("%w: %d <= %v", errShortPacket, len(payload), totalHeaderSize)
}
payloadHeader := newH265NALUHeader(payload[0], payload[1])
if payloadHeader.F() {
return nil, errH265CorruptedPacket
}
if !payloadHeader.IsAggregationPacket() {
return nil, errInvalidH265PacketType
}
// First parse the first aggregation unit
payload = payload[2:]
firstUnit := &H265AggregationUnitFirst{}
if p.mightNeedDONL {
if len(payload) < 2 {
return nil, errShortPacket
}
donl := (uint16(payload[0]) << 8) | uint16(payload[1])
firstUnit.donl = &donl
payload = payload[2:]
}
if len(payload) < 2 {
return nil, errShortPacket
}
firstUnit.nalUnitSize = (uint16(payload[0]) << 8) | uint16(payload[1])
payload = payload[2:]
if len(payload) < int(firstUnit.nalUnitSize) {
return nil, errShortPacket
}
firstUnit.nalUnit = payload[:firstUnit.nalUnitSize]
payload = payload[firstUnit.nalUnitSize:]
// Parse remaining Aggregation Units
var units []H265AggregationUnit
for {
unit := H265AggregationUnit{}
if p.mightNeedDONL {
if len(payload) < 1 {
break
}
dond := payload[0]
unit.dond = &dond
payload = payload[1:]
}
if len(payload) < 2 {
break
}
unit.nalUnitSize = (uint16(payload[0]) << 8) | uint16(payload[1])
payload = payload[2:]
if len(payload) < int(unit.nalUnitSize) {
break
}
unit.nalUnit = payload[:unit.nalUnitSize]
payload = payload[unit.nalUnitSize:]
units = append(units, unit)
}
// There need to be **at least** two Aggregation Units (first + another one)
if len(units) == 0 {
return nil, errShortPacket
}
p.firstUnit = firstUnit
p.otherUnits = units
return nil, nil
}
// FirstUnit returns the first Aggregated Unit of the packet.
func (p *H265AggregationPacket) FirstUnit() *H265AggregationUnitFirst {
return p.firstUnit
}
// OtherUnits returns the all the other Aggregated Unit of the packet (excluding the first one).
func (p *H265AggregationPacket) OtherUnits() []H265AggregationUnit {
return p.otherUnits
}
func (p *H265AggregationPacket) isH265Packet() {}
//
// Fragmentation Unit implementation
//
const (
// sizeof(uint8)
h265FragmentationUnitHeaderSize = 1
)
// H265FragmentationUnitHeader is a H265 FU Header
// +---------------+
// |0|1|2|3|4|5|6|7|
// +-+-+-+-+-+-+-+-+
// |S|E| FuType |
// +---------------+
type H265FragmentationUnitHeader uint8
// S represents the start of a fragmented NAL unit.
func (h H265FragmentationUnitHeader) S() bool {
const mask = 0b10000000
return ((h & mask) >> 7) != 0
}
// E represents the end of a fragmented NAL unit.
func (h H265FragmentationUnitHeader) E() bool {
const mask = 0b01000000
return ((h & mask) >> 6) != 0
}
// FuType MUST be equal to the field Type of the fragmented NAL unit.
func (h H265FragmentationUnitHeader) FuType() uint8 {
const mask = 0b00111111
return uint8(h) & mask
}
// H265FragmentationUnitPacket represents a single Fragmentation Unit packet.
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | PayloadHdr (Type=49) | FU header | DONL (cond) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
// | DONL (cond) | |
// |-+-+-+-+-+-+-+-+ |
// | FU payload |
// | |
// | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | :...OPTIONAL RTP padding |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.3
type H265FragmentationUnitPacket struct {
// payloadHeader is the header of the H265 packet.
payloadHeader H265NALUHeader
// fuHeader is the header of the fragmentation unit
fuHeader H265FragmentationUnitHeader
// donl is a 16-bit field, that may or may not be present.
donl *uint16
// payload of the fragmentation unit.
payload []byte
mightNeedDONL bool
}
// WithDONL can be called to specify whether or not DONL might be parsed.
// DONL may need to be parsed if `sprop-max-don-diff` is greater than 0 on the RTP stream.
func (p *H265FragmentationUnitPacket) WithDONL(value bool) {
p.mightNeedDONL = value
}
// Unmarshal parses the passed byte slice and stores the result in the H265FragmentationUnitPacket this method is called upon.
func (p *H265FragmentationUnitPacket) Unmarshal(payload []byte) ([]byte, error) {
// sizeof(headers)
const totalHeaderSize = h265NaluHeaderSize + h265FragmentationUnitHeaderSize
if payload == nil {
return nil, errNilPacket
} else if len(payload) <= totalHeaderSize {
return nil, fmt.Errorf("%w: %d <= %v", errShortPacket, len(payload), totalHeaderSize)
}
payloadHeader := newH265NALUHeader(payload[0], payload[1])
if payloadHeader.F() {
return nil, errH265CorruptedPacket
}
if !payloadHeader.IsFragmentationUnit() {
return nil, errInvalidH265PacketType
}
fuHeader := H265FragmentationUnitHeader(payload[2])
payload = payload[3:]
if fuHeader.S() && p.mightNeedDONL {
// sizeof(uint16)
if len(payload) <= 2 {
return nil, errShortPacket
}
donl := (uint16(payload[0]) << 8) | uint16(payload[1])
p.donl = &donl
payload = payload[2:]
}
p.payloadHeader = payloadHeader
p.fuHeader = fuHeader
p.payload = payload
return nil, nil
}
// PayloadHeader returns the NALU header of the packet.
func (p *H265FragmentationUnitPacket) PayloadHeader() H265NALUHeader {
return p.payloadHeader
}
// FuHeader returns the Fragmentation Unit Header of the packet.
func (p *H265FragmentationUnitPacket) FuHeader() H265FragmentationUnitHeader {
return p.fuHeader
}
// DONL returns the DONL of the packet.
func (p *H265FragmentationUnitPacket) DONL() *uint16 {
return p.donl
}
// Payload returns the Fragmentation Unit packet payload.
func (p *H265FragmentationUnitPacket) Payload() []byte {
return p.payload
}
func (p *H265FragmentationUnitPacket) isH265Packet() {}
//
// PACI implementation
//
// H265PACIPacket represents a single H265 PACI packet.
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | PayloadHdr (Type=50) |A| cType | PHSsize |F0..2|Y|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Payload Header Extension Structure (PHES) |
// |=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=|
// | |
// | PACI payload: NAL unit |
// | . . . |
// | |
// | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | :...OPTIONAL RTP padding |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.4
type H265PACIPacket struct {
// payloadHeader is the header of the H265 packet.
payloadHeader H265NALUHeader
// Field which holds value for `A`, `cType`, `PHSsize`, `F0`, `F1`, `F2` and `Y` fields.
paciHeaderFields uint16
// phes is a header extension, of byte length `PHSsize`
phes []byte
// Payload contains NAL units & optional padding
payload []byte
}
// PayloadHeader returns the NAL Unit Header.
func (p *H265PACIPacket) PayloadHeader() H265NALUHeader {
return p.payloadHeader
}
// A copies the F bit of the PACI payload NALU.
func (p *H265PACIPacket) A() bool {
const mask = 0b10000000 << 8
return (p.paciHeaderFields & mask) != 0
}
// CType copies the Type field of the PACI payload NALU.
func (p *H265PACIPacket) CType() uint8 {
const mask = 0b01111110 << 8
return uint8((p.paciHeaderFields & mask) >> (8 + 1))
}
// PHSsize indicates the size of the PHES field.
func (p *H265PACIPacket) PHSsize() uint8 {
const mask = (0b00000001 << 8) | 0b11110000
return uint8((p.paciHeaderFields & mask) >> 4)
}
// F0 indicates the presence of a Temporal Scalability support extension in the PHES.
func (p *H265PACIPacket) F0() bool {
const mask = 0b00001000
return (p.paciHeaderFields & mask) != 0
}
// F1 must be zero, reserved for future extensions.
func (p *H265PACIPacket) F1() bool {
const mask = 0b00000100
return (p.paciHeaderFields & mask) != 0
}
// F2 must be zero, reserved for future extensions.
func (p *H265PACIPacket) F2() bool {
const mask = 0b00000010
return (p.paciHeaderFields & mask) != 0
}
// Y must be zero, reserved for future extensions.
func (p *H265PACIPacket) Y() bool {
const mask = 0b00000001
return (p.paciHeaderFields & mask) != 0
}
// PHES contains header extensions. Its size is indicated by PHSsize.
func (p *H265PACIPacket) PHES() []byte {
return p.phes
}
// Payload is a single NALU or NALU-like struct, not including the first two octets (header).
func (p *H265PACIPacket) Payload() []byte {
return p.payload
}
// TSCI returns the Temporal Scalability Control Information extension, if present.
func (p *H265PACIPacket) TSCI() *H265TSCI {
if !p.F0() || p.PHSsize() < 3 {
return nil
}
tsci := H265TSCI((uint32(p.phes[0]) << 16) | (uint32(p.phes[1]) << 8) | uint32(p.phes[0]))
return &tsci
}
// Unmarshal parses the passed byte slice and stores the result in the H265PACIPacket this method is called upon.
func (p *H265PACIPacket) Unmarshal(payload []byte) ([]byte, error) {
// sizeof(headers)
const totalHeaderSize = h265NaluHeaderSize + 2
if payload == nil {
return nil, errNilPacket
} else if len(payload) <= totalHeaderSize {
return nil, fmt.Errorf("%w: %d <= %v", errShortPacket, len(payload), totalHeaderSize)
}
payloadHeader := newH265NALUHeader(payload[0], payload[1])
if payloadHeader.F() {
return nil, errH265CorruptedPacket
}
if !payloadHeader.IsPACIPacket() {
return nil, errInvalidH265PacketType
}
paciHeaderFields := (uint16(payload[2]) << 8) | uint16(payload[3])
payload = payload[4:]
p.paciHeaderFields = paciHeaderFields
headerExtensionSize := p.PHSsize()
if len(payload) < int(headerExtensionSize)+1 {
p.paciHeaderFields = 0
return nil, errShortPacket
}
p.payloadHeader = payloadHeader
if headerExtensionSize > 0 {
p.phes = payload[:headerExtensionSize]
}
payload = payload[headerExtensionSize:]
p.payload = payload
return nil, nil
}
func (p *H265PACIPacket) isH265Packet() {}
//
// Temporal Scalability Control Information
//
// H265TSCI is a Temporal Scalability Control Information header extension.
// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.5
type H265TSCI uint32
// TL0PICIDX see RFC7798 for more details.
func (h H265TSCI) TL0PICIDX() uint8 {
const m1 = 0xFFFF0000
const m2 = 0xFF00
return uint8((((h & m1) >> 16) & m2) >> 8)
}
// IrapPicID see RFC7798 for more details.
func (h H265TSCI) IrapPicID() uint8 {
const m1 = 0xFFFF0000
const m2 = 0x00FF
return uint8(((h & m1) >> 16) & m2)
}
// S see RFC7798 for more details.
func (h H265TSCI) S() bool {
const m1 = 0xFF00
const m2 = 0b10000000
return (uint8((h&m1)>>8) & m2) != 0
}
// E see RFC7798 for more details.
func (h H265TSCI) E() bool {
const m1 = 0xFF00
const m2 = 0b01000000
return (uint8((h&m1)>>8) & m2) != 0
}
// RES see RFC7798 for more details.
func (h H265TSCI) RES() uint8 {
const m1 = 0xFF00
const m2 = 0b00111111
return uint8((h&m1)>>8) & m2
}
//
// H265 Packet interface
//
type isH265Packet interface {
isH265Packet()
}
var (
_ isH265Packet = (*H265FragmentationUnitPacket)(nil)
_ isH265Packet = (*H265PACIPacket)(nil)
_ isH265Packet = (*H265SingleNALUnitPacket)(nil)
_ isH265Packet = (*H265AggregationPacket)(nil)
)
//
// Packet implementation
//
// H265Packet represents a H265 packet, stored in the payload of an RTP packet.
type H265Packet struct {
packet isH265Packet
mightNeedDONL bool
videoDepacketizer
}
// WithDONL can be called to specify whether or not DONL might be parsed.
// DONL may need to be parsed if `sprop-max-don-diff` is greater than 0 on the RTP stream.
func (p *H265Packet) WithDONL(value bool) {
p.mightNeedDONL = value
}
// Unmarshal parses the passed byte slice and stores the result in the H265Packet this method is called upon
func (p *H265Packet) Unmarshal(payload []byte) ([]byte, error) {
if payload == nil {
return nil, errNilPacket
} else if len(payload) <= h265NaluHeaderSize {
return nil, fmt.Errorf("%w: %d <= %v", errShortPacket, len(payload), h265NaluHeaderSize)
}
payloadHeader := newH265NALUHeader(payload[0], payload[1])
if payloadHeader.F() {
return nil, errH265CorruptedPacket
}
switch {
case payloadHeader.IsPACIPacket():
decoded := &H265PACIPacket{}
if _, err := decoded.Unmarshal(payload); err != nil {
return nil, err
}
p.packet = decoded
case payloadHeader.IsFragmentationUnit():
decoded := &H265FragmentationUnitPacket{}
decoded.WithDONL(p.mightNeedDONL)
if _, err := decoded.Unmarshal(payload); err != nil {
return nil, err
}
p.packet = decoded
case payloadHeader.IsAggregationPacket():
decoded := &H265AggregationPacket{}
decoded.WithDONL(p.mightNeedDONL)
if _, err := decoded.Unmarshal(payload); err != nil {
return nil, err
}
p.packet = decoded
default:
decoded := &H265SingleNALUnitPacket{}
decoded.WithDONL(p.mightNeedDONL)
if _, err := decoded.Unmarshal(payload); err != nil {
return nil, err
}
p.packet = decoded
}
return nil, nil
}
// Packet returns the populated packet.
// Must be casted to one of:
// - *H265SingleNALUnitPacket
// - *H265FragmentationUnitPacket
// - *H265AggregationPacket
// - *H265PACIPacket
// nolint:golint
func (p *H265Packet) Packet() isH265Packet {
return p.packet
}
// IsPartitionHead checks if this is the head of a packetized nalu stream.
func (*H265Packet) IsPartitionHead(payload []byte) bool {
if len(payload) < 3 {
return false
}
if H265NALUHeader(binary.BigEndian.Uint16(payload[0:2])).Type() == h265NaluFragmentationUnitType {
return H265FragmentationUnitHeader(payload[2]).S()
}
return true
}

View file

@ -4,7 +4,7 @@ package codecs
type OpusPayloader struct{}
// Payload fragments an Opus packet across one or more byte arrays
func (p *OpusPayloader) Payload(mtu int, payload []byte) [][]byte {
func (p *OpusPayloader) Payload(mtu uint16, payload []byte) [][]byte {
if payload == nil {
return [][]byte{}
}
@ -17,6 +17,8 @@ func (p *OpusPayloader) Payload(mtu int, payload []byte) [][]byte {
// OpusPacket represents the Opus header that is stored in the payload of an RTP Packet
type OpusPacket struct {
Payload []byte
audioDepacketizer
}
// Unmarshal parses the passed byte slice and stores the result in the OpusPacket this method is called upon
@ -31,14 +33,5 @@ func (p *OpusPacket) Unmarshal(packet []byte) ([]byte, error) {
return packet, nil
}
// OpusPartitionHeadChecker checks Opus partition head
// OpusPartitionHeadChecker is obsolete
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
}

View file

@ -1,14 +1,17 @@
package codecs
// VP8Payloader payloads VP8 packets
type VP8Payloader struct{}
type VP8Payloader struct {
EnablePictureID bool
pictureID uint16
}
const (
vp8HeaderSize = 1
)
// Payload fragments a VP8 packet across one or more byte arrays
func (p *VP8Payloader) Payload(mtu int, payload []byte) [][]byte {
func (p *VP8Payloader) Payload(mtu uint16, payload []byte) [][]byte {
/*
* https://tools.ietf.org/html/rfc7741#section-4.2
*
@ -30,7 +33,18 @@ func (p *VP8Payloader) Payload(mtu int, payload []byte) [][]byte {
* first packet of each encoded frame.
*/
maxFragmentSize := mtu - vp8HeaderSize
usingHeaderSize := vp8HeaderSize
if p.EnablePictureID {
switch {
case p.pictureID == 0:
case p.pictureID < 128:
usingHeaderSize = vp8HeaderSize + 2
default:
usingHeaderSize = vp8HeaderSize + 3
}
}
maxFragmentSize := int(mtu) - usingHeaderSize
payloadData := payload
payloadDataRemaining := len(payload)
@ -42,40 +56,67 @@ func (p *VP8Payloader) Payload(mtu int, payload []byte) [][]byte {
if min(maxFragmentSize, payloadDataRemaining) <= 0 {
return payloads
}
first := true
for payloadDataRemaining > 0 {
currentFragmentSize := min(maxFragmentSize, payloadDataRemaining)
out := make([]byte, vp8HeaderSize+currentFragmentSize)
if payloadDataRemaining == len(payload) {
out := make([]byte, usingHeaderSize+currentFragmentSize)
if first {
out[0] = 0x10
first = false
}
if p.EnablePictureID {
switch usingHeaderSize {
case vp8HeaderSize:
case vp8HeaderSize + 2:
out[0] |= 0x80
out[1] |= 0x80
out[2] |= uint8(p.pictureID & 0x7F)
case vp8HeaderSize + 3:
out[0] |= 0x80
out[1] |= 0x80
out[2] |= 0x80 | uint8((p.pictureID>>8)&0x7F)
out[3] |= uint8(p.pictureID & 0xFF)
}
}
copy(out[vp8HeaderSize:], payloadData[payloadDataIndex:payloadDataIndex+currentFragmentSize])
copy(out[usingHeaderSize:], payloadData[payloadDataIndex:payloadDataIndex+currentFragmentSize])
payloads = append(payloads, out)
payloadDataRemaining -= currentFragmentSize
payloadDataIndex += currentFragmentSize
}
p.pictureID++
p.pictureID &= 0x7FFF
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 */
X uint8 /* extended control bits present */
N uint8 /* 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 */
// Extended control bits
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 */
// Optional extension
PictureID uint16 /* 8 or 16 bits, picture ID */
TL0PICIDX uint8 /* 8 bits temporal level zero index */
TID uint8 /* 2 bits temporal layer index */
Y uint8 /* 1 bit layer sync bit */
KEYIDX uint8 /* 5 bits temporal key frame index */
Payload []byte
videoDepacketizer
}
// Unmarshal parses the passed byte slice and stores the result in the VP8Packet this method is called upon
@ -109,17 +150,35 @@ func (p *VP8Packet) Unmarshal(payload []byte) ([]byte, error) {
if p.I == 1 { // PID present?
if payload[payloadIndex]&0x80 > 0 { // M == 1, PID is 16bit
p.PictureID = (uint16(payload[payloadIndex]&0x7F) << 8) | uint16(payload[payloadIndex+1])
payloadIndex += 2
} else {
p.PictureID = uint16(payload[payloadIndex])
payloadIndex++
}
}
if payloadIndex >= payloadLen {
return nil, errShortPacket
}
if p.L == 1 {
p.TL0PICIDX = payload[payloadIndex]
payloadIndex++
}
if payloadIndex >= payloadLen {
return nil, errShortPacket
}
if p.T == 1 || p.K == 1 {
if p.T == 1 {
p.TID = payload[payloadIndex] >> 6
p.Y = (payload[payloadIndex] >> 5) & 0x1
}
if p.K == 1 {
p.KEYIDX = payload[payloadIndex] & 0x1F
}
payloadIndex++
}
@ -130,14 +189,13 @@ func (p *VP8Packet) Unmarshal(payload []byte) ([]byte, error) {
return p.Payload, nil
}
// VP8PartitionHeadChecker checks VP8 partition head
// VP8PartitionHeadChecker is obsolete
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 {
func (*VP8Packet) IsPartitionHead(payload []byte) bool {
if len(payload) < 1 {
return false
}
return p.S == 1
return (payload[0] & 0x10) != 0
}

View file

@ -23,14 +23,14 @@ const (
)
// Payload fragments an VP9 packet across one or more byte arrays
func (p *VP9Payloader) Payload(mtu int, payload []byte) [][]byte {
func (p *VP9Payloader) Payload(mtu uint16, payload []byte) [][]byte {
/*
* https://www.ietf.org/id/draft-ietf-payload-vp9-10.txt
* https://www.ietf.org/id/draft-ietf-payload-vp9-13.txt
*
* Flexible mode (F=1)
* 0 1 2 3 4 5 6 7
* +-+-+-+-+-+-+-+-+
* |I|P|L|F|B|E|V|-| (REQUIRED)
* |I|P|L|F|B|E|V|Z| (REQUIRED)
* +-+-+-+-+-+-+-+-+
* I: |M| PICTURE ID | (REQUIRED)
* +-+-+-+-+-+-+-+-+
@ -47,7 +47,7 @@ func (p *VP9Payloader) Payload(mtu int, payload []byte) [][]byte {
* Non-flexible mode (F=0)
* 0 1 2 3 4 5 6 7
* +-+-+-+-+-+-+-+-+
* |I|P|L|F|B|E|V|-| (REQUIRED)
* |I|P|L|F|B|E|V|Z| (REQUIRED)
* +-+-+-+-+-+-+-+-+
* I: |M| PICTURE ID | (RECOMMENDED)
* +-+-+-+-+-+-+-+-+
@ -75,7 +75,7 @@ func (p *VP9Payloader) Payload(mtu int, payload []byte) [][]byte {
return [][]byte{}
}
maxFragmentSize := mtu - vp9HeaderSize
maxFragmentSize := int(mtu) - vp9HeaderSize
payloadDataRemaining := len(payload)
payloadDataIndex := 0
@ -121,6 +121,7 @@ type VP9Packet struct {
B bool // Start of a frame
E bool // End of a frame
V bool // Scalability structure (SS) data present
Z bool // Not a reference frame for upper spatial layers
// Recommended headers
PictureID uint16 // 7 or 16 bits, picture ID
@ -147,6 +148,8 @@ type VP9Packet struct {
PGPDiff [][]uint8 // Reference indecies of pictures in a Picture Group
Payload []byte
videoDepacketizer
}
// Unmarshal parses the passed byte slice and stores the result in the VP9Packet this method is called upon
@ -165,6 +168,7 @@ func (p *VP9Packet) Unmarshal(packet []byte) ([]byte, error) {
p.B = packet[0]&0x08 != 0
p.E = packet[0]&0x04 != 0
p.V = packet[0]&0x02 != 0
p.Z = packet[0]&0x01 != 0
pos := 1
var err error
@ -372,14 +376,13 @@ func (p *VP9Packet) parseSSData(packet []byte, pos int) (int, error) {
return pos, nil
}
// VP9PartitionHeadChecker checks VP9 partition head
// VP9PartitionHeadChecker is obsolete
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 {
func (*VP9Packet) IsPartitionHead(payload []byte) bool {
if len(payload) < 1 {
return false
}
return p.B
return (payload[0] & 0x08) != 0
}

View file

@ -3,4 +3,11 @@ package rtp
// Depacketizer depacketizes a RTP payload, removing any RTP specific data from the payload
type Depacketizer interface {
Unmarshal(packet []byte) ([]byte, error)
// Checks if the packet is at the beginning of a partition. This
// should return false if the result could not be determined, in
// which case the caller will detect timestamp discontinuities.
IsPartitionHead(payload []byte) bool
// Checks if the packet is at the end of a partition. This should
// return false if the result could not be determined.
IsPartitionTail(marker bool, payload []byte) bool
}

View file

@ -1,5 +0,0 @@
module github.com/pion/rtp
go 1.13
require github.com/pion/randutil v0.1.0

View file

@ -1,2 +0,0 @@
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=

View file

@ -0,0 +1,350 @@
package rtp
import (
"encoding/binary"
"fmt"
"io"
)
const (
headerExtensionProfileOneByte = 0xBEDE
headerExtensionProfileTwoByte = 0x1000
headerExtensionIDReserved = 0xF
)
// HeaderExtension represents an RTP extension header.
type HeaderExtension interface {
Set(id uint8, payload []byte) error
GetIDs() []uint8
Get(id uint8) []byte
Del(id uint8) error
Unmarshal(buf []byte) (int, error)
Marshal() ([]byte, error)
MarshalTo(buf []byte) (int, error)
MarshalSize() int
}
// OneByteHeaderExtension is an RFC8285 one-byte header extension.
type OneByteHeaderExtension struct {
payload []byte
}
// Set sets the extension payload for the specified ID.
func (e *OneByteHeaderExtension) Set(id uint8, buf []byte) error {
if id < 1 || id > 14 {
return fmt.Errorf("%w actual(%d)", errRFC8285OneByteHeaderIDRange, id)
}
if len(buf) > 16 {
return fmt.Errorf("%w actual(%d)", errRFC8285OneByteHeaderSize, len(buf))
}
for n := 4; n < len(e.payload); {
if e.payload[n] == 0x00 { // padding
n++
continue
}
extid := e.payload[n] >> 4
len := int(e.payload[n]&^0xF0 + 1)
n++
if extid == id {
e.payload = append(e.payload[:n+1], append(buf, e.payload[n+1+len:]...)...)
return nil
}
n += len
}
e.payload = append(e.payload, (id<<4 | uint8(len(buf)-1)))
e.payload = append(e.payload, buf...)
binary.BigEndian.PutUint16(e.payload[2:4], binary.BigEndian.Uint16(e.payload[2:4])+1)
return nil
}
// GetIDs returns the available IDs.
func (e *OneByteHeaderExtension) GetIDs() []uint8 {
ids := make([]uint8, 0, binary.BigEndian.Uint16(e.payload[2:4]))
for n := 4; n < len(e.payload); {
if e.payload[n] == 0x00 { // padding
n++
continue
}
extid := e.payload[n] >> 4
len := int(e.payload[n]&^0xF0 + 1)
n++
if extid == headerExtensionIDReserved {
break
}
ids = append(ids, extid)
n += len
}
return ids
}
// Get returns the payload of the extension with the given ID.
func (e *OneByteHeaderExtension) Get(id uint8) []byte {
for n := 4; n < len(e.payload); {
if e.payload[n] == 0x00 { // padding
n++
continue
}
extid := e.payload[n] >> 4
len := int(e.payload[n]&^0xF0 + 1)
n++
if extid == id {
return e.payload[n : n+len]
}
n += len
}
return nil
}
// Del deletes the extension with the specified ID.
func (e *OneByteHeaderExtension) Del(id uint8) error {
for n := 4; n < len(e.payload); {
if e.payload[n] == 0x00 { // padding
n++
continue
}
extid := e.payload[n] >> 4
len := int(e.payload[n]&^0xF0 + 1)
if extid == id {
e.payload = append(e.payload[:n], e.payload[n+1+len:]...)
return nil
}
n += len + 1
}
return errHeaderExtensionNotFound
}
// Unmarshal parses the extension payload.
func (e *OneByteHeaderExtension) Unmarshal(buf []byte) (int, error) {
profile := binary.BigEndian.Uint16(buf[0:2])
if profile != headerExtensionProfileOneByte {
return 0, fmt.Errorf("%w actual(%x)", errHeaderExtensionNotFound, buf[0:2])
}
e.payload = buf
return len(buf), nil
}
// Marshal returns the extension payload.
func (e OneByteHeaderExtension) Marshal() ([]byte, error) {
return e.payload, nil
}
// MarshalTo writes the extension payload to the given buffer.
func (e OneByteHeaderExtension) MarshalTo(buf []byte) (int, error) {
size := e.MarshalSize()
if size > len(buf) {
return 0, io.ErrShortBuffer
}
return copy(buf, e.payload), nil
}
// MarshalSize returns the size of the extension payload.
func (e OneByteHeaderExtension) MarshalSize() int {
return len(e.payload)
}
// TwoByteHeaderExtension is an RFC8285 two-byte header extension.
type TwoByteHeaderExtension struct {
payload []byte
}
// Set sets the extension payload for the specified ID.
func (e *TwoByteHeaderExtension) Set(id uint8, buf []byte) error {
if id < 1 || id > 255 {
return fmt.Errorf("%w actual(%d)", errRFC8285TwoByteHeaderIDRange, id)
}
if len(buf) > 255 {
return fmt.Errorf("%w actual(%d)", errRFC8285TwoByteHeaderSize, len(buf))
}
for n := 4; n < len(e.payload); {
if e.payload[n] == 0x00 { // padding
n++
continue
}
extid := e.payload[n]
n++
len := int(e.payload[n])
n++
if extid == id {
e.payload = append(e.payload[:n+2], append(buf, e.payload[n+2+len:]...)...)
return nil
}
n += len
}
e.payload = append(e.payload, id, uint8(len(buf)))
e.payload = append(e.payload, buf...)
binary.BigEndian.PutUint16(e.payload[2:4], binary.BigEndian.Uint16(e.payload[2:4])+1)
return nil
}
// GetIDs returns the available IDs.
func (e *TwoByteHeaderExtension) GetIDs() []uint8 {
ids := make([]uint8, 0, binary.BigEndian.Uint16(e.payload[2:4]))
for n := 4; n < len(e.payload); {
if e.payload[n] == 0x00 { // padding
n++
continue
}
extid := e.payload[n]
n++
len := int(e.payload[n])
n++
ids = append(ids, extid)
n += len
}
return ids
}
// Get returns the payload of the extension with the given ID.
func (e *TwoByteHeaderExtension) Get(id uint8) []byte {
for n := 4; n < len(e.payload); {
if e.payload[n] == 0x00 { // padding
n++
continue
}
extid := e.payload[n]
n++
len := int(e.payload[n])
n++
if extid == id {
return e.payload[n : n+len]
}
n += len
}
return nil
}
// Del deletes the extension with the specified ID.
func (e *TwoByteHeaderExtension) Del(id uint8) error {
for n := 4; n < len(e.payload); {
if e.payload[n] == 0x00 { // padding
n++
continue
}
extid := e.payload[n]
len := int(e.payload[n+1])
if extid == id {
e.payload = append(e.payload[:n], e.payload[n+2+len:]...)
return nil
}
n += len + 2
}
return errHeaderExtensionNotFound
}
// Unmarshal parses the extension payload.
func (e *TwoByteHeaderExtension) Unmarshal(buf []byte) (int, error) {
profile := binary.BigEndian.Uint16(buf[0:2])
if profile != headerExtensionProfileTwoByte {
return 0, fmt.Errorf("%w actual(%x)", errHeaderExtensionNotFound, buf[0:2])
}
e.payload = buf
return len(buf), nil
}
// Marshal returns the extension payload.
func (e TwoByteHeaderExtension) Marshal() ([]byte, error) {
return e.payload, nil
}
// MarshalTo marshals the extension to the given buffer.
func (e TwoByteHeaderExtension) MarshalTo(buf []byte) (int, error) {
size := e.MarshalSize()
if size > len(buf) {
return 0, io.ErrShortBuffer
}
return copy(buf, e.payload), nil
}
// MarshalSize returns the size of the extension payload.
func (e TwoByteHeaderExtension) MarshalSize() int {
return len(e.payload)
}
// RawExtension represents an RFC3550 header extension.
type RawExtension struct {
payload []byte
}
// Set sets the extension payload for the specified ID.
func (e *RawExtension) Set(id uint8, payload []byte) error {
if id != 0 {
return fmt.Errorf("%w actual(%d)", errRFC3550HeaderIDRange, id)
}
e.payload = payload
return nil
}
// GetIDs returns the available IDs.
func (e *RawExtension) GetIDs() []uint8 {
return []uint8{0}
}
// Get returns the payload of the extension with the given ID.
func (e *RawExtension) Get(id uint8) []byte {
if id == 0 {
return e.payload
}
return nil
}
// Del deletes the extension with the specified ID.
func (e *RawExtension) Del(id uint8) error {
if id == 0 {
e.payload = nil
return nil
}
return fmt.Errorf("%w actual(%d)", errRFC3550HeaderIDRange, id)
}
// Unmarshal parses the extension from the given buffer.
func (e *RawExtension) Unmarshal(buf []byte) (int, error) {
profile := binary.BigEndian.Uint16(buf[0:2])
if profile == headerExtensionProfileOneByte || profile == headerExtensionProfileTwoByte {
return 0, fmt.Errorf("%w actual(%x)", errHeaderExtensionNotFound, buf[0:2])
}
e.payload = buf
return len(buf), nil
}
// Marshal returns the raw extension payload.
func (e RawExtension) Marshal() ([]byte, error) {
return e.payload, nil
}
// MarshalTo marshals the extension to the given buffer.
func (e RawExtension) MarshalTo(buf []byte) (int, error) {
size := e.MarshalSize()
if size > len(buf) {
return 0, io.ErrShortBuffer
}
return copy(buf, e.payload), nil
}
// MarshalSize returns the size of the extension when marshaled.
func (e RawExtension) MarshalSize() int {
return len(e.payload)
}

View file

@ -13,13 +13,11 @@ type Extension struct {
}
// Header represents an RTP packet header
// NOTE: PayloadOffset is populated by Marshal/Unmarshal and should not be modified
type Header struct {
Version uint8
Padding bool
Extension bool
Marker bool
PayloadOffset int
PayloadType uint8
SequenceNumber uint16
Timestamp uint32
@ -30,11 +28,10 @@ type Header struct {
}
// Packet represents an RTP Packet
// NOTE: Raw is populated by Marshal/Unmarshal and should not be modified
type Packet struct {
Header
Raw []byte
Payload []byte
Payload []byte
PaddingSize byte
}
const (
@ -77,10 +74,11 @@ func (p Packet) String() string {
return out
}
// Unmarshal parses the passed byte slice and stores the result in the Header this method is called upon
func (h *Header) Unmarshal(rawPacket []byte) error { //nolint:gocognit
if len(rawPacket) < headerLength {
return fmt.Errorf("%w: %d < %d", errHeaderSizeInsufficient, len(rawPacket), headerLength)
// Unmarshal parses the passed byte slice and stores the result in the Header.
// It returns the number of bytes read n and any error.
func (h *Header) Unmarshal(buf []byte) (n int, err error) { //nolint:gocognit
if len(buf) < headerLength {
return 0, fmt.Errorf("%w: %d < %d", errHeaderSizeInsufficient, len(buf), headerLength)
}
/*
@ -98,31 +96,32 @@ func (h *Header) Unmarshal(rawPacket []byte) error { //nolint:gocognit
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
h.Version = rawPacket[0] >> versionShift & versionMask
h.Padding = (rawPacket[0] >> paddingShift & paddingMask) > 0
h.Extension = (rawPacket[0] >> extensionShift & extensionMask) > 0
nCSRC := int(rawPacket[0] & ccMask)
h.Version = buf[0] >> versionShift & versionMask
h.Padding = (buf[0] >> paddingShift & paddingMask) > 0
h.Extension = (buf[0] >> extensionShift & extensionMask) > 0
nCSRC := int(buf[0] & ccMask)
if cap(h.CSRC) < nCSRC || h.CSRC == nil {
h.CSRC = make([]uint32, nCSRC)
} else {
h.CSRC = h.CSRC[:nCSRC]
}
currOffset := csrcOffset + (nCSRC * csrcLength)
if len(rawPacket) < currOffset {
return fmt.Errorf("size %d < %d: %w", len(rawPacket), currOffset, errHeaderSizeInsufficient)
n = csrcOffset + (nCSRC * csrcLength)
if len(buf) < n {
return n, fmt.Errorf("size %d < %d: %w", len(buf), n,
errHeaderSizeInsufficient)
}
h.Marker = (rawPacket[1] >> markerShift & markerMask) > 0
h.PayloadType = rawPacket[1] & ptMask
h.Marker = (buf[1] >> markerShift & markerMask) > 0
h.PayloadType = buf[1] & ptMask
h.SequenceNumber = binary.BigEndian.Uint16(rawPacket[seqNumOffset : seqNumOffset+seqNumLength])
h.Timestamp = binary.BigEndian.Uint32(rawPacket[timestampOffset : timestampOffset+timestampLength])
h.SSRC = binary.BigEndian.Uint32(rawPacket[ssrcOffset : ssrcOffset+ssrcLength])
h.SequenceNumber = binary.BigEndian.Uint16(buf[seqNumOffset : seqNumOffset+seqNumLength])
h.Timestamp = binary.BigEndian.Uint32(buf[timestampOffset : timestampOffset+timestampLength])
h.SSRC = binary.BigEndian.Uint32(buf[ssrcOffset : ssrcOffset+ssrcLength])
for i := range h.CSRC {
offset := csrcOffset + (i * csrcLength)
h.CSRC[i] = binary.BigEndian.Uint32(rawPacket[offset:])
h.CSRC[i] = binary.BigEndian.Uint32(buf[offset:])
}
if h.Extensions != nil {
@ -130,21 +129,21 @@ func (h *Header) Unmarshal(rawPacket []byte) error { //nolint:gocognit
}
if h.Extension {
if expected := currOffset + 4; len(rawPacket) < expected {
return fmt.Errorf("size %d < %d: %w",
len(rawPacket), expected,
if expected := n + 4; len(buf) < expected {
return n, fmt.Errorf("size %d < %d: %w",
len(buf), expected,
errHeaderSizeInsufficientForExtension,
)
}
h.ExtensionProfile = binary.BigEndian.Uint16(rawPacket[currOffset:])
currOffset += 2
extensionLength := int(binary.BigEndian.Uint16(rawPacket[currOffset:])) * 4
currOffset += 2
h.ExtensionProfile = binary.BigEndian.Uint16(buf[n:])
n += 2
extensionLength := int(binary.BigEndian.Uint16(buf[n:])) * 4
n += 2
if expected := currOffset + extensionLength; len(rawPacket) < expected {
return fmt.Errorf("size %d < %d: %w",
len(rawPacket), expected,
if expected := n + extensionLength; len(buf) < expected {
return n, fmt.Errorf("size %d < %d: %w",
len(buf), expected,
errHeaderSizeInsufficientForExtension,
)
}
@ -152,87 +151,91 @@ func (h *Header) Unmarshal(rawPacket []byte) error { //nolint:gocognit
switch h.ExtensionProfile {
// RFC 8285 RTP One Byte Header Extension
case extensionProfileOneByte:
end := currOffset + extensionLength
for currOffset < end {
if rawPacket[currOffset] == 0x00 { // padding
currOffset++
end := n + extensionLength
for n < end {
if buf[n] == 0x00 { // padding
n++
continue
}
extid := rawPacket[currOffset] >> 4
len := int(rawPacket[currOffset]&^0xF0 + 1)
currOffset++
extid := buf[n] >> 4
len := int(buf[n]&^0xF0 + 1)
n++
if extid == extensionIDReserved {
break
}
extension := Extension{id: extid, payload: rawPacket[currOffset : currOffset+len]}
extension := Extension{id: extid, payload: buf[n : n+len]}
h.Extensions = append(h.Extensions, extension)
currOffset += len
n += len
}
// RFC 8285 RTP Two Byte Header Extension
case extensionProfileTwoByte:
end := currOffset + extensionLength
for currOffset < end {
if rawPacket[currOffset] == 0x00 { // padding
currOffset++
end := n + extensionLength
for n < end {
if buf[n] == 0x00 { // padding
n++
continue
}
extid := rawPacket[currOffset]
currOffset++
extid := buf[n]
n++
len := int(rawPacket[currOffset])
currOffset++
len := int(buf[n])
n++
extension := Extension{id: extid, payload: rawPacket[currOffset : currOffset+len]}
extension := Extension{id: extid, payload: buf[n : n+len]}
h.Extensions = append(h.Extensions, extension)
currOffset += len
n += len
}
default: // RFC3550 Extension
if len(rawPacket) < currOffset+extensionLength {
return fmt.Errorf("%w: %d < %d", errHeaderSizeInsufficientForExtension, len(rawPacket), currOffset+extensionLength)
if len(buf) < n+extensionLength {
return n, fmt.Errorf("%w: %d < %d",
errHeaderSizeInsufficientForExtension, len(buf), n+extensionLength)
}
extension := Extension{id: 0, payload: rawPacket[currOffset : currOffset+extensionLength]}
extension := Extension{id: 0, payload: buf[n : n+extensionLength]}
h.Extensions = append(h.Extensions, extension)
currOffset += len(h.Extensions[0].payload)
n += len(h.Extensions[0].payload)
}
}
h.PayloadOffset = currOffset
return nil
return n, nil
}
// Unmarshal parses the passed byte slice and stores the result in the Packet this method is called upon
func (p *Packet) Unmarshal(rawPacket []byte) error {
if err := p.Header.Unmarshal(rawPacket); err != nil {
// Unmarshal parses the passed byte slice and stores the result in the Packet.
func (p *Packet) Unmarshal(buf []byte) error {
n, err := p.Header.Unmarshal(buf)
if err != nil {
return err
}
p.Payload = rawPacket[p.PayloadOffset:]
p.Raw = rawPacket
end := len(buf)
if p.Header.Padding {
p.PaddingSize = buf[end-1]
end -= int(p.PaddingSize)
}
if end < n {
return errTooSmall
}
p.Payload = buf[n:end]
return nil
}
// Marshal serializes the header into bytes.
func (h *Header) Marshal() (buf []byte, err error) {
func (h Header) Marshal() (buf []byte, err error) {
buf = make([]byte, h.MarshalSize())
n, err := h.MarshalTo(buf)
if err != nil {
return nil, err
}
return buf[:n], nil
}
// MarshalTo serializes the header and writes to the buffer.
func (h *Header) MarshalTo(buf []byte) (n int, err error) {
func (h Header) MarshalTo(buf []byte) (n int, err error) {
/*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
@ -253,7 +256,8 @@ func (h *Header) MarshalTo(buf []byte) (n int, err error) {
return 0, io.ErrShortBuffer
}
// The first byte contains the version, padding bit, extension bit, and csrc size
// The first byte contains the version, padding bit, extension bit,
// and csrc size.
buf[0] = (h.Version << versionShift) | uint8(len(h.CSRC))
if h.Padding {
buf[0] |= 1 << paddingShift
@ -324,13 +328,11 @@ func (h *Header) MarshalTo(buf []byte) (n int, err error) {
}
}
h.PayloadOffset = n
return n, nil
}
// MarshalSize returns the size of the header once marshaled.
func (h *Header) MarshalSize() int {
func (h Header) MarshalSize() int {
// NOTE: Be careful to match the MarshalTo() method.
size := 12 + (len(h.CSRC) * csrcLength)
@ -455,7 +457,7 @@ func (h *Header) DelExtension(id uint8) error {
}
// Marshal serializes the packet into bytes.
func (p *Packet) Marshal() (buf []byte, err error) {
func (p Packet) Marshal() (buf []byte, err error) {
buf = make([]byte, p.MarshalSize())
n, err := p.MarshalTo(buf)
@ -467,24 +469,60 @@ func (p *Packet) Marshal() (buf []byte, err error) {
}
// MarshalTo serializes the packet and writes to the buffer.
func (p *Packet) MarshalTo(buf []byte) (n int, err error) {
func (p Packet) MarshalTo(buf []byte) (n int, err error) {
p.Header.Padding = p.PaddingSize != 0
n, err = p.Header.MarshalTo(buf)
if err != nil {
return 0, err
}
// Make sure the buffer is large enough to hold the packet.
if n+len(p.Payload) > len(buf) {
if n+len(p.Payload)+int(p.PaddingSize) > len(buf) {
return 0, io.ErrShortBuffer
}
m := copy(buf[n:], p.Payload)
p.Raw = buf[:n+m]
if p.Header.Padding {
buf[n+m+int(p.PaddingSize-1)] = p.PaddingSize
}
return n + m, nil
return n + m + int(p.PaddingSize), nil
}
// MarshalSize returns the size of the packet once marshaled.
func (p *Packet) MarshalSize() int {
return p.Header.MarshalSize() + len(p.Payload)
func (p Packet) MarshalSize() int {
return p.Header.MarshalSize() + len(p.Payload) + int(p.PaddingSize)
}
// Clone returns a deep copy of p.
func (p Packet) Clone() *Packet {
clone := &Packet{}
clone.Header = p.Header.Clone()
if p.Payload != nil {
clone.Payload = make([]byte, len(p.Payload))
copy(clone.Payload, p.Payload)
}
clone.PaddingSize = p.PaddingSize
return clone
}
// Clone returns a deep copy h.
func (h Header) Clone() Header {
clone := h
if h.CSRC != nil {
clone.CSRC = make([]uint32, len(h.CSRC))
copy(clone.CSRC, h.CSRC)
}
if h.Extensions != nil {
ext := make([]Extension, len(h.Extensions))
for i, e := range h.Extensions {
ext[i] = e
if e.payload != nil {
ext[i].payload = make([]byte, len(e.payload))
copy(ext[i].payload, e.payload)
}
}
clone.Extensions = ext
}
return clone
}

View file

@ -6,17 +6,18 @@ import (
// Payloader payloads a byte array for use as rtp.Packet payloads
type Payloader interface {
Payload(mtu int, payload []byte) [][]byte
Payload(mtu uint16, payload []byte) [][]byte
}
// Packetizer packetizes a payload
type Packetizer interface {
Packetize(payload []byte, samples uint32) []*Packet
EnableAbsSendTime(value int)
SkipSamples(skippedSamples uint32)
}
type packetizer struct {
MTU int
MTU uint16
PayloadType uint8
SSRC uint32
Payloader Payloader
@ -30,7 +31,7 @@ type packetizer struct {
}
// NewPacketizer returns a new instance of a Packetizer for a specific payloader
func NewPacketizer(mtu int, pt uint8, ssrc uint32, payloader Payloader, sequencer Sequencer, clockRate uint32) Packetizer {
func NewPacketizer(mtu uint16, pt uint8, ssrc uint32, payloader Payloader, sequencer Sequencer, clockRate uint32) Packetizer {
return &packetizer{
MTU: mtu,
PayloadType: pt,
@ -89,3 +90,9 @@ func (p *packetizer) Packetize(payload []byte, samples uint32) []*Packet {
return packets
}
// SkipSamples causes a gap in sample count between Packetize requests so the
// RTP payloads produced have a gap in timestamps
func (p *packetizer) SkipSamples(skippedSamples uint32) {
p.Timestamp += skippedSamples
}

View file

@ -0,0 +1,44 @@
// Package frame provides code to construct complete media frames from packetized media
package frame
import "github.com/pion/rtp/codecs"
// AV1 represents a collection of OBUs given a stream of AV1 Packets.
// Each AV1 RTP Packet is a collection of OBU Elements. Each OBU Element may be a full OBU, or just a fragment of one.
// AV1 provides the tools to construct a collection of OBUs from a collection of OBU Elements. This structure
// contains an internal cache and should be used for the entire RTP Stream.
type AV1 struct {
// Buffer for fragmented OBU. If ReadFrames is called on a RTP Packet
// that doesn't contain a fully formed OBU
obuBuffer []byte
}
func (f *AV1) pushOBUElement(isFirstOBUFragment *bool, obuElement []byte, obuList [][]byte) [][]byte {
if *isFirstOBUFragment {
*isFirstOBUFragment = false
// Discard pushed because we don't have a fragment to combine it with
if f.obuBuffer == nil {
return obuList
}
obuElement = append(f.obuBuffer, obuElement...)
f.obuBuffer = nil
}
return append(obuList, obuElement)
}
// ReadFrames processes the codecs.AV1Packet and returns fully constructed frames
func (f *AV1) ReadFrames(pkt *codecs.AV1Packet) ([][]byte, error) {
OBUs := [][]byte{}
isFirstOBUFragment := pkt.Z
for i := range pkt.OBUElements {
OBUs = f.pushOBUElement(&isFirstOBUFragment, pkt.OBUElements[i], OBUs)
}
if pkt.Y && len(OBUs) > 0 {
// Take copy of OBUElement that is being cached
f.obuBuffer = append(f.obuBuffer, append([]byte{}, OBUs[len(OBUs)-1]...)...)
OBUs = OBUs[:len(OBUs)-1]
}
return OBUs, nil
}

View file

@ -0,0 +1,66 @@
// Package obu implements tools for working with the "Open Bitstream Unit"
package obu
import "errors"
const (
sevenLsbBitmask = uint(0b01111111)
msbBitmask = uint(0b10000000)
)
// ErrFailedToReadLEB128 indicates that a buffer ended before a LEB128 value could be successfully read
var ErrFailedToReadLEB128 = errors.New("payload ended before LEB128 was finished")
// EncodeLEB128 encodes a uint as LEB128
func EncodeLEB128(in uint) (out uint) {
for {
// Copy seven bits from in and discard
// what we have copied from in
out |= (in & sevenLsbBitmask)
in >>= 7
// If we have more bits to encode set MSB
// otherwise we are done
if in != 0 {
out |= msbBitmask
out <<= 8
} else {
return out
}
}
}
func decodeLEB128(in uint) (out uint) {
for {
// Take 7 LSB from in
out |= (in & sevenLsbBitmask)
// Discard the MSB
in >>= 8
if in == 0 {
return out
}
out <<= 7
}
}
// ReadLeb128 scans an buffer and decodes a Leb128 value.
// If the end of the buffer is reached and all MSB are set
// an error is returned
func ReadLeb128(in []byte) (uint, uint, error) {
var encodedLength uint
for i := range in {
encodedLength |= uint(in[i])
if in[i]&byte(msbBitmask) == 0 {
return decodeLEB128(encodedLength), uint(i + 1), nil
}
// Make more room for next read
encodedLength <<= 8
}
return 0, 0, ErrFailedToReadLEB128
}

View file

@ -1,15 +1,27 @@
{
"extends": [
"config:base"
"config:base",
":disableDependencyDashboard"
],
"postUpdateOptions": [
"gomodTidy"
],
"commitBody": "Generated by renovateBot",
"packageRules": [
{
"matchUpdateTypes": ["minor", "patch", "pin", "digest"],
"automerge": true
},
{
"packagePatterns": ["^golang.org/x/"],
"schedule": ["on the first day of the month"]
}
],
"ignorePaths": [
".github/workflows/generate-authors.yml",
".github/workflows/lint.yaml",
".github/workflows/renovate-go-mod-fix.yaml",
".github/workflows/test.yaml",
".github/workflows/tidy-check.yaml"
]
}

View file

@ -23,7 +23,7 @@ type TransportCCExtension struct {
}
// Marshal serializes the members to buffer
func (t *TransportCCExtension) Marshal() ([]byte, error) {
func (t TransportCCExtension) Marshal() ([]byte, error) {
buf := make([]byte, transportCCExtensionSize)
binary.BigEndian.PutUint16(buf[0:2], t.TransportSequence)
return buf, nil