Skip to content

Commit

Permalink
Finish AV1 Implementation
Browse files Browse the repository at this point in the history
1. Rewrite the AV1 Payloader according to the spec.
2. AV1 bitstream reader.
3. Tries to optimize the use of the W field as much as possible to
    reduce the size of the packet.
4. Added tests around edge cases.
  • Loading branch information
JoeTurki committed Mar 1, 2025
1 parent 061df8a commit 342e9d9
Show file tree
Hide file tree
Showing 4 changed files with 2,101 additions and 144 deletions.
13 changes: 5 additions & 8 deletions codecs/av1_depacketizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ func (d *AV1Depacketizer) Unmarshal(payload []byte) (buff []byte, err error) {
}

// |Z|Y| W |N|-|-|-|
obuZ := (0b10000000 & payload[0]) != 0 // Z
obuY := (0b01000000 & payload[0]) != 0 // Y
obuCount := (0b00110000 & payload[0]) >> 4 // W
obuN := (0b00001000 & payload[0]) != 0 // N
obuZ := (av1ZMask & payload[0]) != 0 // Z
obuY := (av1YMask & payload[0]) != 0 // Y
obuCount := (av1WMask & payload[0]) >> 4 // W
obuN := (av1NMask & payload[0]) != 0 // N
d.Z = obuZ
d.Y = obuY
d.N = obuN
Expand Down Expand Up @@ -120,10 +120,7 @@ func (d *AV1Depacketizer) Unmarshal(payload []byte) (buff []byte, err error) {
}

if len(obuBuffer) == 0 {
return nil, fmt.Errorf(
"%w: OBU size %d is 0",
errShortPacket, lengthField,
)
continue
}

obuHeader, err := obu.ParseOBUHeader(obuBuffer)
Expand Down
61 changes: 39 additions & 22 deletions codecs/av1_depacketizer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,12 @@ func createTestPayload(obuHeader obu.Header, payload []byte) []byte {
return buf
}

func TestAV1Depacketizerr_invalidPackets(t *testing.T) {
func TestAV1Depacketizer_invalidPackets(t *testing.T) {
depacketizer := AV1Depacketizer{}
_, err := depacketizer.Unmarshal([]byte{})
if !errors.Is(err, errShortPacket) {
t.Fatalf("Unexpected error: %v", err)
}

_, err = depacketizer.Unmarshal([]byte{0x00})
if !errors.Is(err, errShortPacket) {
t.Fatalf("Unexpected error: %v", err)
}

_, err = depacketizer.Unmarshal([]byte{0b11000000, 0xFF})
if !errors.Is(err, obu.ErrFailedToReadLEB128) {
t.Fatalf("Unexpected error: %v", err)
Expand All @@ -55,7 +49,7 @@ func TestAV1Depacketizerr_invalidPackets(t *testing.T) {
t.Fatalf("Unexpected error: %v", err)
}

_, err = depacketizer.Unmarshal(append([]byte{0b00000000}, obu.WriteToLeb128(0)...))
_, err = depacketizer.Unmarshal(append([]byte{0b00000000}, obu.WriteToLeb128(0x01)...))
if !errors.Is(err, errShortPacket) {
t.Fatalf("Unexpected error: %v", err)
}
Expand All @@ -74,7 +68,28 @@ func TestAV1Depacketizerr_invalidPackets(t *testing.T) {
}
}

func TestAV1Depacketizerr_singleOBU(t *testing.T) {
func TestAV1Depacketizer_singleOBU(t *testing.T) {
payload := []byte{0x01, 0x02, 0x03}
obuData, expectedOBU := createAV1OBU(4, payload)

packet := make([]byte, 0)

packet = append(packet, []byte{0b00000000}...)
packet = append(packet, obu.WriteToLeb128(uint(len(obuData)))...)
packet = append(packet, obuData...)

d := AV1Depacketizer{}
obu, err := d.Unmarshal(packet)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}

if !bytes.Equal(obu, expectedOBU) {
t.Fatalf("OBU data mismatch, expected %v, got %v", expectedOBU, obu)
}
}

func TestAV1Depacketizer_singleOBUWithPadding(t *testing.T) {
payload := []byte{0x01, 0x02, 0x03}
obuData, expectedOBU := createAV1OBU(4, payload)

Expand All @@ -83,6 +98,8 @@ func TestAV1Depacketizerr_singleOBU(t *testing.T) {
packet = append(packet, []byte{0b00000000}...)
packet = append(packet, obu.WriteToLeb128(uint(len(obuData)))...)
packet = append(packet, obuData...)
// padding
packet = append(packet, []byte{0x00, 0x00, 0x00}...)

d := AV1Depacketizer{}
obu, err := d.Unmarshal(packet)
Expand All @@ -97,7 +114,7 @@ func TestAV1Depacketizerr_singleOBU(t *testing.T) {

// AV1 OBUs shouldn't include the obu_size_field when packetized in RTP,
// but we still support it since it's encountered in the wild (Including pion old clients).
func TestAV1Depacketizerr_withOBUSize(t *testing.T) {
func TestAV1Depacketizer_withOBUSize(t *testing.T) {
payload := []byte{0x01, 0x02, 0x03}
_, obuData := createAV1OBU(4, payload)

Expand All @@ -118,7 +135,7 @@ func TestAV1Depacketizerr_withOBUSize(t *testing.T) {
}
}

func TestAV1Depacketizerr_validateOBUSize(t *testing.T) {
func TestAV1Depacketizer_validateOBUSize(t *testing.T) {
tests := []struct {
name string
payload []byte
Expand Down Expand Up @@ -169,7 +186,7 @@ func TestAV1Depacketizerr_validateOBUSize(t *testing.T) {
}
}

func TestAV1Depacketizerr_dropBuffer(t *testing.T) {
func TestAV1Depacketizer_dropBuffer(t *testing.T) {
depacketizer := &AV1Depacketizer{}
empty, err := depacketizer.Unmarshal([]byte{0x41, 0x02, 0x00, 0x01})
if err != nil {
Expand Down Expand Up @@ -200,7 +217,7 @@ func TestAV1Depacketizerr_dropBuffer(t *testing.T) {
}
}

func TestAV1Depacketizerr_singleOBUWithW(t *testing.T) {
func TestAV1Depacketizer_singleOBUWithW(t *testing.T) {
payload := []byte{0x01, 0x02, 0x03}
obuData, expectedOBU := createAV1OBU(4, payload)

Expand Down Expand Up @@ -244,7 +261,7 @@ func TestDepacketizer_multipleFullOBUs(t *testing.T) {
}
}

func TestAV1Depacketizerr_multipleFullOBUsWithW(t *testing.T) {
func TestAV1Depacketizer_multipleFullOBUsWithW(t *testing.T) {
obu1, expectedOBU1 := createAV1OBU(4, []byte{0x01, 0x02, 0x03})
obu2, expectedOBU2 := createAV1OBU(4, []byte{0x04, 0x05, 0x06})
obu3, expectedOBU3 := createAV1OBU(4, []byte{0x07, 0x08, 0x09})
Expand Down Expand Up @@ -367,7 +384,7 @@ func TestDepacketizer_fragmentedOBUS(t *testing.T) {
}
}

func TestAV1Depacketizerr_dropLostFragment(t *testing.T) {
func TestAV1Depacketizer_dropLostFragment(t *testing.T) {
depacketizer := AV1Depacketizer{}

obus, err := depacketizer.Unmarshal(
Expand Down Expand Up @@ -400,7 +417,7 @@ func TestAV1Depacketizerr_dropLostFragment(t *testing.T) {
}
}

func TestAV1Depacketizerr_dropIfLostFragment(t *testing.T) {
func TestAV1Depacketizer_dropIfLostFragment(t *testing.T) {
depacketizer := AV1Depacketizer{}

obus, err := depacketizer.Unmarshal(
Expand Down Expand Up @@ -449,7 +466,7 @@ func TestAV1Depacketizerr_dropIfLostFragment(t *testing.T) {
}
}

func TestAV1Depacketizerr_IsPartitionTail(t *testing.T) {
func TestAV1Depacketizer_IsPartitionTail(t *testing.T) {
depacketizer := &AV1Depacketizer{
buffer: []byte{1, 2},
}
Expand All @@ -467,7 +484,7 @@ func TestAV1Depacketizerr_IsPartitionTail(t *testing.T) {
}
}

func TestAV1Depacketizerr_IsPartitionHead(t *testing.T) {
func TestAV1Depacketizer_IsPartitionHead(t *testing.T) {
depacketizer := &AV1Depacketizer{}

if depacketizer.IsPartitionHead(nil) {
Expand All @@ -487,7 +504,7 @@ func TestAV1Depacketizerr_IsPartitionHead(t *testing.T) {
}
}

func TestAV1Depacketizerr_ignoreBadOBUs(t *testing.T) {
func TestAV1Depacketizer_ignoreBadOBUs(t *testing.T) {
shouldIgnore := []obu.Type{
obu.OBUTemporalDelimiter,
obu.OBUTileList,
Expand All @@ -514,7 +531,7 @@ func TestAV1Depacketizerr_ignoreBadOBUs(t *testing.T) {
}
}

func TestAV1Depacketizerr_fragmentedOverMultiple(t *testing.T) {
func TestAV1Depacketizer_fragmentedOverMultiple(t *testing.T) {
fullOBU, expected := createAV1OBU(
obu.OBUTileGroup,
[]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08},
Expand Down Expand Up @@ -583,7 +600,7 @@ func TestAV1Depacketizerr_fragmentedOverMultiple(t *testing.T) {
}
}

func TestAV1Depacketizerr_shortOBUHeader(t *testing.T) {
func TestAV1Depacketizer_shortOBUHeader(t *testing.T) {
d := AV1Depacketizer{}

payload, err := d.Unmarshal([]byte{0x00, 0x01, 0x04})
Expand All @@ -596,7 +613,7 @@ func TestAV1Depacketizerr_shortOBUHeader(t *testing.T) {
}
}

func TestAV1Depacketizerr_aggregationHeader(t *testing.T) {
func TestAV1Depacketizer_aggregationHeader(t *testing.T) {
depacketizer := AV1Depacketizer{}
tests := []struct {
name string
Expand Down
Loading

0 comments on commit 342e9d9

Please sign in to comment.