Skip to content

Commit

Permalink
Make parsing of the OBU list in AV1Packet optional
Browse files Browse the repository at this point in the history
In Pion, the (*XXXPacket).Unmarshal operation is supposed to be
fast and perform no allocation, as it is sometimes used to check
just a single flag in the packet header.  The AV1Packet
implementation breaks this property by parsing the whole list
of OBUs at Unmarshal time, even though it is likely to be unneeded.

The proper solution would be to remove the parsing of OBUs from
Unmarshal, for consistency with the other packet formats.
Unfortunately, this would break backwards compatibility.  We
therefore introduce a field DontParseOBUs which controls the
parsing.  Unfortunately, this means that the inefficient path
is the default, which smells really badly, but there's not much
we can do about it at this stage.
  • Loading branch information
jech committed Apr 15, 2024
1 parent 74a9dc7 commit 0ab1260
Showing 1 changed file with 35 additions and 8 deletions.
43 changes: 35 additions & 8 deletions codecs/av1_packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,22 @@ type AV1Packet struct {
// 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

// If true, don't populate the OBUElements field.
dontParseBody 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
//
// Deprecated: use the ParseOBUElements method instead.
OBUElements [][]byte
}

// SetDontParseBody specifies whether to skip populating the OBUElements field.
// Applications should set this to true and use the ParseBody function instead.
func (p *AV1Packet) SetDontParseBody(dont bool) {
p.dontParseBody = true
}

// 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 {
Expand All @@ -153,13 +164,28 @@ func (p *AV1Packet) Unmarshal(payload []byte) ([]byte, error) {
return nil, errIsKeyframeAndFragment
}

currentIndex := uint(1)
p.OBUElements = [][]byte{}
if !p.dontParseBody {
elts, err := p.ParseBody(payload[1:])
if err != nil {
return nil, err
}
p.OBUElements = elts
}

return payload[1:], nil
}

// ParseOBUElements returns the list of OBUs in p
func (p *AV1Packet) ParseBody(payload []byte) ([][]byte, error) {
if p.OBUElements != nil {
return p.OBUElements, nil
}

elts := [][]byte{}

var (
obuElementLength, bytesRead uint
err error
)
obuElementLength := uint(0)
bytesRead := uint(0)
currentIndex := uint(0)
for i := 1; ; i++ {
if currentIndex == uint(len(payload)) {
break
Expand All @@ -170,6 +196,7 @@ func (p *AV1Packet) Unmarshal(payload []byte) ([]byte, error) {
bytesRead = 0
obuElementLength = uint(len(payload)) - currentIndex
} else {
var err error
obuElementLength, bytesRead, err = obu.ReadLeb128(payload[currentIndex:])
if err != nil {
return nil, err
Expand All @@ -180,9 +207,9 @@ func (p *AV1Packet) Unmarshal(payload []byte) ([]byte, error) {
if uint(len(payload)) < currentIndex+obuElementLength {
return nil, errShortPacket
}
p.OBUElements = append(p.OBUElements, payload[currentIndex:currentIndex+obuElementLength])
elts = append(elts, payload[currentIndex:currentIndex+obuElementLength])
currentIndex += obuElementLength
}

return payload[1:], nil
return elts, nil
}

0 comments on commit 0ab1260

Please sign in to comment.