Skip to content

Commit

Permalink
Merge pull request #287 from tanghaowillow/add-av1
Browse files Browse the repository at this point in the history
feat: add av01 and av1C box
  • Loading branch information
tobbee authored Oct 18, 2023
2 parents e10c8a0 + 61a5f11 commit 89ae7d7
Show file tree
Hide file tree
Showing 7 changed files with 246 additions and 0 deletions.
116 changes: 116 additions & 0 deletions av1/av1codecconfigurationrecord.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package av1

import (
"errors"
"io"

"github.com/Eyevinn/mp4ff/bits"
)

// AV1 parsing errors
var (
ErrInvalidMarker = errors.New("invalid marker value found in AV1CodecConfigurationRecord")
ErrInvalidVersion = errors.New("unsupported AV1CodecConfigurationRecord version")
ErrNonZeroReservedBits = errors.New("non-zero reserved bits found in AV1CodecConfigurationRecord")
)

// CodecConfRec - AV1CodecConfigurationRecord
// Specified in https://github.com/AOMediaCodec/av1-isobmff/releases/tag/v1.2.0
type CodecConfRec struct {
Version byte
SeqProfile byte
SeqLevelIdx0 byte
SeqTier0 byte
HighBitdepth byte
TwelveBit byte
MonoChrome byte
ChromaSubsamplingX byte
ChromaSubsamplingY byte
ChromaSamplePosition byte
InitialPresentationDelayPresent byte
InitialPresentationDelayMinusOne byte
ConfigOBUs []byte
}

// DecodeAVCDecConfRec - decode an AV1CodecConfRec
func DecodeAV1CodecConfRec(data []byte) (CodecConfRec, error) {
av1drc := CodecConfRec{}

Marker := data[0] >> 7
if Marker != 1 {
return CodecConfRec{}, ErrInvalidMarker
}
av1drc.Version = data[0] & 0x7F
if av1drc.Version != 1 {
return CodecConfRec{}, ErrInvalidVersion
}
av1drc.SeqProfile = data[1] >> 5
av1drc.SeqLevelIdx0 = data[1] & 0x1F
av1drc.SeqTier0 = data[2] >> 7
av1drc.HighBitdepth = (data[2] >> 6) & 0x01
av1drc.TwelveBit = (data[2] >> 5) & 0x01
av1drc.MonoChrome = (data[2] >> 4) & 0x01
av1drc.ChromaSubsamplingX = (data[2] >> 3) & 0x01
av1drc.ChromaSubsamplingY = (data[2] >> 2) & 0x01
av1drc.ChromaSamplePosition = data[2] & 0x03
if data[3]>>5 != 0 {
return CodecConfRec{}, ErrNonZeroReservedBits
}
av1drc.InitialPresentationDelayPresent = (data[3] >> 4) & 0x01
if av1drc.InitialPresentationDelayPresent == 1 {
av1drc.InitialPresentationDelayMinusOne = data[3] & 0x0F
} else {
if data[3]&0x0F != 0 {
return CodecConfRec{}, ErrNonZeroReservedBits
}
av1drc.InitialPresentationDelayMinusOne = 0
}
if len(data) > 4 {
av1drc.ConfigOBUs = data[4:]
}

return av1drc, nil
}

// Size - total size in bytes
func (a *CodecConfRec) Size() uint64 {
return uint64(4 + len(a.ConfigOBUs))
}

// EncodeSW- write an AV1CodecConfRec to w
func (a *CodecConfRec) Encode(w io.Writer) error {
sw := bits.NewFixedSliceWriter(int(a.Size()))
err := a.EncodeSW(sw)
if err != nil {
return err
}
_, err = w.Write(sw.Bytes())
return err
}

// EncodeSW- write an AV1CodecConfRec to sw
func (a *CodecConfRec) EncodeSW(sw bits.SliceWriter) error {
sw.WriteBits(1, 1)
sw.WriteBits(uint(a.Version), 7)
sw.WriteBits(uint(a.SeqProfile), 3)
sw.WriteBits(uint(a.SeqLevelIdx0), 5)
sw.WriteBits(uint(a.SeqTier0), 1)
sw.WriteBits(uint(a.HighBitdepth), 1)
sw.WriteBits(uint(a.TwelveBit), 1)
sw.WriteBits(uint(a.MonoChrome), 1)
sw.WriteBits(uint(a.ChromaSubsamplingX), 1)
sw.WriteBits(uint(a.ChromaSubsamplingY), 1)
sw.WriteBits(uint(a.ChromaSamplePosition), 2)
sw.WriteBits(0, 3)
sw.WriteBits(uint(a.InitialPresentationDelayPresent), 1)
if a.InitialPresentationDelayPresent == 1 {
sw.WriteBits(uint(a.InitialPresentationDelayMinusOne), 4)
} else {
sw.WriteBits(0, 4)
}
if len(a.ConfigOBUs) != 0 {
sw.WriteBytes(a.ConfigOBUs)
}

return sw.AccError()
}
40 changes: 40 additions & 0 deletions av1/av1codecconfigurationrecord_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package av1

import (
"encoding/hex"
"testing"

"github.com/go-test/deep"
)

const av1DecoderConfigRecord = "81094c000a0b0000004aabbfc377ffe701"
const configOBUs = "0a0b0000004aabbfc377ffe701"

func TestDecodeAV1DecConfRec(t *testing.T) {
byteData, _ := hex.DecodeString(av1DecoderConfigRecord)
configOBUsBytes, _ := hex.DecodeString(configOBUs)

wanted := CodecConfRec{
Version: 1,
SeqProfile: 0,
SeqLevelIdx0: 9,
SeqTier0: 0,
HighBitdepth: 1,
TwelveBit: 0,
MonoChrome: 0,
ChromaSubsamplingX: 1,
ChromaSubsamplingY: 1,
ChromaSamplePosition: 0,
InitialPresentationDelayPresent: 0,
InitialPresentationDelayMinusOne: 0,
ConfigOBUs: configOBUsBytes,
}

got, err := DecodeAV1CodecConfRec(byteData)
if err != nil {
t.Error("Error parsing Av1DecoderConfigRecord")
}
if diff := deep.Equal(got, wanted); diff != nil {
t.Error(diff)
}
}
4 changes: 4 additions & 0 deletions av1/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/*
Package av1 - parsing of av1 AV1CodecConfigurationRecord.
*/
package av1
79 changes: 79 additions & 0 deletions mp4/av1c.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package mp4

import (
"encoding/hex"
"io"

"github.com/Eyevinn/mp4ff/av1"
"github.com/Eyevinn/mp4ff/bits"
)

type Av1CBox struct {
av1.CodecConfRec
}

// DecodeAv1C - box-specific decode
func DecodeAv1C(hdr BoxHeader, startPos uint64, r io.Reader) (Box, error) {
data, err := readBoxBody(r, hdr)
if err != nil {
return nil, err
}
av1DecConfRec, err := av1.DecodeAV1CodecConfRec(data)
if err != nil {
return nil, err
}
return &Av1CBox{av1DecConfRec}, nil
}

// DecodeAv1CSR - box-specific decode
func DecodeAv1CSR(hdr BoxHeader, startPos uint64, sr bits.SliceReader) (Box, error) {
av1DecConfRec, err := av1.DecodeAV1CodecConfRec(sr.ReadBytes(hdr.payloadLen()))
return &Av1CBox{av1DecConfRec}, err
}

// Type - return box type
func (b *Av1CBox) Type() string {
return "av1C"
}

// Size - return calculated size
func (b *Av1CBox) Size() uint64 {
return uint64(boxHeaderSize + b.CodecConfRec.Size())
}

// Encode - write box to w
func (b *Av1CBox) Encode(w io.Writer) error {
err := EncodeHeader(b, w)
if err != nil {
return err
}
return b.CodecConfRec.Encode(w)
}

// Encode - write box to sw
func (b *Av1CBox) EncodeSW(sw bits.SliceWriter) error {
err := EncodeHeaderSW(b, sw)
if err != nil {
return err
}
return b.CodecConfRec.EncodeSW(sw)
}

// Info - box-specific Info
func (b *Av1CBox) Info(w io.Writer, specificBoxLevels, indent, indentStep string) error {
bd := newInfoDumper(w, indent, b, -1, 0)
bd.write(" - SeqProfile: %d", b.SeqProfile)
bd.write(" - SeqLevelIdx0: %d", b.SeqLevelIdx0)
bd.write(" - SeqTier0: %d", b.SeqTier0)
bd.write(" - HighBitdepth: %d", b.HighBitdepth)
bd.write(" - TwelveBit: %d", b.TwelveBit)
bd.write(" - MonoChrome: %d", b.MonoChrome)
bd.write(" - ChromaSubsamplingX: %d", b.ChromaSubsamplingX)
bd.write(" - ChromaSubsamplingY: %d", b.ChromaSubsamplingY)
bd.write(" - ChromaSamplePosition: %d", b.ChromaSamplePosition)
if b.InitialPresentationDelayPresent == 1 {
bd.write(" - InitialPresentationDelayMinusOne: %d", b.InitialPresentationDelayMinusOne)
}
bd.write(" - ConfigOBUs: %s", hex.EncodeToString(b.ConfigOBUs))
return bd.err
}
2 changes: 2 additions & 0 deletions mp4/box.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ func init() {
decoders = map[string]BoxDecoder{
"ac-3": DecodeAudioSampleEntry,
"alou": DecodeAlou,
"av01": DecodeVisualSampleEntry,
"avc1": DecodeVisualSampleEntry,
"avc3": DecodeVisualSampleEntry,
"avcC": DecodeAvcC,
"av1C": DecodeAv1C,
"btrt": DecodeBtrt,
"cdat": DecodeCdat,
"cdsc": DecodeTrefType,
Expand Down
2 changes: 2 additions & 0 deletions mp4/boxsr.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ var decodersSR map[string]BoxDecoderSR
func init() {
decodersSR = map[string]BoxDecoderSR{
"ac-3": DecodeAudioSampleEntrySR,
"av01": DecodeVisualSampleEntrySR,
"avc1": DecodeVisualSampleEntrySR,
"avc3": DecodeVisualSampleEntrySR,
"alou": DecodeAlouBoxSR,
"avcC": DecodeAvcCSR,
"av1C": DecodeAv1CSR,
"btrt": DecodeBtrtSR,
"cdat": DecodeCdatSR,
"cdsc": DecodeTrefTypeSR,
Expand Down
3 changes: 3 additions & 0 deletions mp4/visualsampleentry.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type VisualSampleEntryBox struct {
CompressorName string
AvcC *AvcCBox
HvcC *HvcCBox
Av1C *Av1CBox
Btrt *BtrtBox
Clap *ClapBox
Pasp *PaspBox
Expand Down Expand Up @@ -60,6 +61,8 @@ func (b *VisualSampleEntryBox) AddChild(child Box) {
b.AvcC = box
case *HvcCBox:
b.HvcC = box
case *Av1CBox:
b.Av1C = box
case *BtrtBox:
b.Btrt = box
case *ClapBox:
Expand Down

0 comments on commit 89ae7d7

Please sign in to comment.