diff --git a/codecs/av1/frame/av1.go b/codecs/av1/frame/av1.go new file mode 100644 index 0000000..1e001a3 --- /dev/null +++ b/codecs/av1/frame/av1.go @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +// 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 +} diff --git a/pkg/frame/av1_test.go b/codecs/av1/frame/av1_test.go similarity index 100% rename from pkg/frame/av1_test.go rename to codecs/av1/frame/av1_test.go diff --git a/codecs/av1/obu/leb128.go b/codecs/av1/obu/leb128.go new file mode 100644 index 0000000..38ce090 --- /dev/null +++ b/codecs/av1/obu/leb128.go @@ -0,0 +1,69 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +// 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 +} diff --git a/pkg/obu/leb128_test.go b/codecs/av1/obu/leb128_test.go similarity index 100% rename from pkg/obu/leb128_test.go rename to codecs/av1/obu/leb128_test.go diff --git a/codecs/av1_packet.go b/codecs/av1_packet.go index 3f4761d..3a78b90 100644 --- a/codecs/av1_packet.go +++ b/codecs/av1_packet.go @@ -4,7 +4,7 @@ package codecs import ( - "github.com/pion/rtp/pkg/obu" + "github.com/pion/rtp/codecs/av1/obu" ) const ( diff --git a/codecs/av1_packet_test.go b/codecs/av1_packet_test.go index 711996c..2a65c4f 100644 --- a/codecs/av1_packet_test.go +++ b/codecs/av1_packet_test.go @@ -9,7 +9,7 @@ import ( "reflect" "testing" - "github.com/pion/rtp/pkg/obu" + "github.com/pion/rtp/codecs/av1/obu" ) func TestAV1_Marshal(t *testing.T) { diff --git a/pkg/frame/av1.go b/pkg/frame/av1.go index 0dadde6..73edea6 100644 --- a/pkg/frame/av1.go +++ b/pkg/frame/av1.go @@ -1,47 +1,17 @@ // SPDX-FileCopyrightText: 2023 The Pion community // SPDX-License-Identifier: MIT -// Package frame provides code to construct complete media frames from packetized media +// Package frame is deprecated. package frame -import "github.com/pion/rtp/codecs" +import ( + "github.com/pion/rtp/codecs/av1/frame" +) // 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 -} +// +// Deprecated: moved into codecs/av1/frame. +type AV1 = frame.AV1 diff --git a/pkg/obu/leb128.go b/pkg/obu/leb128.go index f0734f0..e1d9c8c 100644 --- a/pkg/obu/leb128.go +++ b/pkg/obu/leb128.go @@ -1,69 +1,30 @@ // SPDX-FileCopyrightText: 2023 The Pion community // SPDX-License-Identifier: MIT -// Package obu implements tools for working with the "Open Bitstream Unit" +// Package obu is deprecated. package obu -import "errors" - -const ( - sevenLsbBitmask = uint(0b01111111) - msbBitmask = uint(0b10000000) +import ( + "github.com/pion/rtp/codecs/av1/obu" ) // ErrFailedToReadLEB128 indicates that a buffer ended before a LEB128 value could be successfully read -var ErrFailedToReadLEB128 = errors.New("payload ended before LEB128 was finished") +// +// Deprecated: moved into codecs/av1/obu. +var ErrFailedToReadLEB128 = obu.ErrFailedToReadLEB128 // EncodeLEB128 encodes a uint as LEB128 +// +// Deprecated: moved into codecs/av1/obu. 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 - } + return obu.EncodeLEB128(in) } // 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 +// +// Deprecated: moved into codecs/av1/obu. 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 + return obu.ReadLeb128(in) }