From b4c7e9b95f8fe35916239c3f7d03383a7d9c2d71 Mon Sep 17 00:00:00 2001 From: aler9 <46489434+aler9@users.noreply.github.com> Date: Sun, 21 Mar 2021 21:44:01 +0100 Subject: [PATCH] rtpaac: add more test cases to MPEG4AudioConfig --- pkg/rtpaac/mpeg4audioconfig.go | 124 ++++++++++++++++++++++++++++ pkg/rtpaac/mpeg4audioconfig_test.go | 52 ++++++++++++ pkg/rtpaac/rtpaac.go | 106 ------------------------ pkg/rtpaac/rtpaac_test.go | 27 ------ pkg/rtph264/nalutype.go | 102 +++++++++++++++++++++++ pkg/rtph264/rtph264.go | 101 ---------------------- 6 files changed, 278 insertions(+), 234 deletions(-) create mode 100644 pkg/rtpaac/mpeg4audioconfig.go create mode 100644 pkg/rtpaac/mpeg4audioconfig_test.go create mode 100644 pkg/rtph264/nalutype.go diff --git a/pkg/rtpaac/mpeg4audioconfig.go b/pkg/rtpaac/mpeg4audioconfig.go new file mode 100644 index 00000000..2c1f497d --- /dev/null +++ b/pkg/rtpaac/mpeg4audioconfig.go @@ -0,0 +1,124 @@ +package rtpaac + +import ( + "bytes" + "fmt" + + "github.com/icza/bitio" +) + +// MPEG4AudioType is the type of a MPEG-4 Audio stream. +type MPEG4AudioType int + +// standard MPEG-4 Audio types. +const ( + MPEG4AudioTypeAACLC MPEG4AudioType = 2 +) + +// MPEG4AudioConfig is a MPEG-4 Audio configuration. +type MPEG4AudioConfig struct { + Type MPEG4AudioType + SampleRate int + ChannelCount int +} + +// Decode decodes an MPEG-4 Audio configuration. +func (c *MPEG4AudioConfig) Decode(byts []byte) error { + // ref: https://wiki.multimedia.cx/index.php/MPEG-4_Audio + + r := bitio.NewReader(bytes.NewBuffer(byts)) + + tmp, err := r.ReadBits(5) + if err != nil { + return err + } + c.Type = MPEG4AudioType(tmp) + + if tmp == 31 { + tmp, err = r.ReadBits(6) + if err != nil { + return err + } + c.Type = MPEG4AudioType(tmp + 32) + } + + switch c.Type { + case MPEG4AudioTypeAACLC: + default: + return fmt.Errorf("unsupported type: %d", c.Type) + } + + sampleRateIndex, err := r.ReadBits(4) + if err != nil { + return err + } + + switch sampleRateIndex { + case 0: + c.SampleRate = 96000 + case 1: + c.SampleRate = 88200 + case 2: + c.SampleRate = 64000 + case 3: + c.SampleRate = 48000 + case 4: + c.SampleRate = 44100 + case 5: + c.SampleRate = 32000 + case 6: + c.SampleRate = 24000 + case 7: + c.SampleRate = 22050 + case 8: + c.SampleRate = 16000 + case 9: + c.SampleRate = 12000 + case 10: + c.SampleRate = 11025 + case 11: + c.SampleRate = 8000 + case 12: + c.SampleRate = 7350 + + case 15: + sampleRateIndex, err := r.ReadBits(24) + if err != nil { + return err + } + c.SampleRate = int(sampleRateIndex) + + default: + return fmt.Errorf("invalid sample rate index: %d", sampleRateIndex) + } + + channelConfig, err := r.ReadBits(4) + if err != nil { + return err + } + + switch channelConfig { + case 0: + return fmt.Errorf("not yet supported") + + case 1: + c.ChannelCount = 1 + case 2: + c.ChannelCount = 2 + case 3: + c.ChannelCount = 3 + case 4: + c.ChannelCount = 4 + case 5: + c.ChannelCount = 5 + case 6: + c.ChannelCount = 6 + case 7: + c.ChannelCount = 8 + + default: + return fmt.Errorf("invalid channel configuration: %d", channelConfig) + } + + return nil +} diff --git a/pkg/rtpaac/mpeg4audioconfig_test.go b/pkg/rtpaac/mpeg4audioconfig_test.go new file mode 100644 index 00000000..b5402da0 --- /dev/null +++ b/pkg/rtpaac/mpeg4audioconfig_test.go @@ -0,0 +1,52 @@ +package rtpaac + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +var configCases = []struct { + name string + enc []byte + dec MPEG4AudioConfig +}{ + { + name: "aac-lc 48khz stereo", + enc: []byte{17, 144}, + dec: MPEG4AudioConfig{ + Type: MPEG4AudioTypeAACLC, + SampleRate: 48000, + ChannelCount: 2, + }, + }, + { + name: "aac-lc 96khz stereo", + enc: []byte{0x10, 0x10, 0x56, 0xE5, 0x00}, + dec: MPEG4AudioConfig{ + Type: MPEG4AudioTypeAACLC, + SampleRate: 96000, + ChannelCount: 2, + }, + }, + { + name: "aac-lc 44.1khz 5.1", + enc: []byte{0x12, 0x30}, + dec: MPEG4AudioConfig{ + Type: MPEG4AudioTypeAACLC, + SampleRate: 44100, + ChannelCount: 6, + }, + }, +} + +func TestConfigDecode(t *testing.T) { + for _, ca := range configCases { + t.Run(ca.name, func(t *testing.T) { + var dec MPEG4AudioConfig + err := dec.Decode(ca.enc) + require.NoError(t, err) + require.Equal(t, ca.dec, dec) + }) + } +} diff --git a/pkg/rtpaac/rtpaac.go b/pkg/rtpaac/rtpaac.go index 3c50fc29..81222364 100644 --- a/pkg/rtpaac/rtpaac.go +++ b/pkg/rtpaac/rtpaac.go @@ -2,11 +2,7 @@ package rtpaac import ( - "bytes" - "fmt" "time" - - "github.com/icza/bitio" ) // AUAndTimestamp is an Access Unit and its timestamp. @@ -14,105 +10,3 @@ type AUAndTimestamp struct { Timestamp time.Duration AU []byte } - -// MPEG4AudioConfig is a MPEG-4 Audio configuration. -type MPEG4AudioConfig struct { - ObjectType int - SampleRate int - ChannelCount int -} - -// Decode decodes an MPEG-4 Audio configuration. -func (c *MPEG4AudioConfig) Decode(byts []byte) error { - // ref: https://wiki.multimedia.cx/index.php/MPEG-4_Audio - - r := bitio.NewReader(bytes.NewBuffer(byts)) - - tmp, err := r.ReadBits(5) - if err != nil { - return err - } - c.ObjectType = int(tmp) - - if tmp == 31 { - tmp, err = r.ReadBits(6) - if err != nil { - return err - } - c.ObjectType = int(tmp + 32) - } - - sampleRateIndex, err := r.ReadBits(4) - if err != nil { - return err - } - - switch sampleRateIndex { - case 0: - c.SampleRate = 96000 - case 1: - c.SampleRate = 88200 - case 2: - c.SampleRate = 64000 - case 3: - c.SampleRate = 48000 - case 4: - c.SampleRate = 44100 - case 5: - c.SampleRate = 32000 - case 6: - c.SampleRate = 24000 - case 7: - c.SampleRate = 22050 - case 8: - c.SampleRate = 16000 - case 9: - c.SampleRate = 12000 - case 10: - c.SampleRate = 11025 - case 11: - c.SampleRate = 8000 - case 12: - c.SampleRate = 7350 - - case 15: - sampleRateIndex, err := r.ReadBits(24) - if err != nil { - return err - } - c.SampleRate = int(sampleRateIndex) - - default: - return fmt.Errorf("invalid sample rate index: %d", sampleRateIndex) - } - - channelConfig, err := r.ReadBits(4) - if err != nil { - return err - } - - switch channelConfig { - case 0: - return fmt.Errorf("not yet supported") - - case 1: - c.ChannelCount = 1 - case 2: - c.ChannelCount = 2 - case 3: - c.ChannelCount = 3 - case 4: - c.ChannelCount = 4 - case 5: - c.ChannelCount = 5 - case 6: - c.ChannelCount = 6 - case 7: - c.ChannelCount = 8 - - default: - return fmt.Errorf("invalid channel configuration: %d", channelConfig) - } - - return nil -} diff --git a/pkg/rtpaac/rtpaac_test.go b/pkg/rtpaac/rtpaac_test.go index 076e3870..e79f586b 100644 --- a/pkg/rtpaac/rtpaac_test.go +++ b/pkg/rtpaac/rtpaac_test.go @@ -7,33 +7,6 @@ import ( "github.com/stretchr/testify/require" ) -var configCases = []struct { - name string - enc []byte - dec MPEG4AudioConfig -}{ - { - name: "test 1", - enc: []byte{17, 144}, - dec: MPEG4AudioConfig{ - ObjectType: 2, - SampleRate: 48000, - ChannelCount: 2, - }, - }, -} - -func TestConfigDecode(t *testing.T) { - for _, ca := range configCases { - t.Run(ca.name, func(t *testing.T) { - var dec MPEG4AudioConfig - err := dec.Decode(ca.enc) - require.NoError(t, err) - require.Equal(t, ca.dec, dec) - }) - } -} - var cases = []struct { name string dec *AUAndTimestamp diff --git a/pkg/rtph264/nalutype.go b/pkg/rtph264/nalutype.go new file mode 100644 index 00000000..097ec3cd --- /dev/null +++ b/pkg/rtph264/nalutype.go @@ -0,0 +1,102 @@ +package rtph264 + +// NALUType is the type of a NALU. +type NALUType uint8 + +// standard NALU types. +const ( + NALUTypeNonIDR NALUType = 1 + NALUTypeDataPartitionA NALUType = 2 + NALUTypeDataPartitionB NALUType = 3 + NALUTypeDataPartitionC NALUType = 4 + NALUTypeIDR NALUType = 5 + NALUTypeSei NALUType = 6 + NALUTypeSPS NALUType = 7 + NALUTypePPS NALUType = 8 + NALUTypeAccessUnitDelimiter NALUType = 9 + NALUTypeEndOfSequence NALUType = 10 + NALUTypeEndOfStream NALUType = 11 + NALUTypeFillerData NALUType = 12 + NALUTypeSPSExtension NALUType = 13 + NALUTypePrefix NALUType = 14 + NALUTypeSubsetSPS NALUType = 15 + NALUTypeReserved16 NALUType = 16 + NALUTypeReserved17 NALUType = 17 + NALUTypeReserved18 NALUType = 18 + NALUTypeSliceLayerWithoutPartitioning NALUType = 19 + NALUTypeSliceExtension NALUType = 20 + NALUTypeSliceExtensionDepth NALUType = 21 + NALUTypeReserved22 NALUType = 22 + NALUTypeReserved23 NALUType = 23 + NALUTypeStapA NALUType = 24 + NALUTypeStapB NALUType = 25 + NALUTypeMtap16 NALUType = 26 + NALUTypeMtap24 NALUType = 27 + NALUTypeFuA NALUType = 28 + NALUTypeFuB NALUType = 29 +) + +// String implements fmt.Stringer. +func (nt NALUType) String() string { + switch nt { + case NALUTypeNonIDR: + return "NonIDR" + case NALUTypeDataPartitionA: + return "DataPartitionA" + case NALUTypeDataPartitionB: + return "DataPartitionB" + case NALUTypeDataPartitionC: + return "DataPartitionC" + case NALUTypeIDR: + return "IDR" + case NALUTypeSei: + return "Sei" + case NALUTypeSPS: + return "SPS" + case NALUTypePPS: + return "PPS" + case NALUTypeAccessUnitDelimiter: + return "AccessUnitDelimiter" + case NALUTypeEndOfSequence: + return "EndOfSequence" + case NALUTypeEndOfStream: + return "EndOfStream" + case NALUTypeFillerData: + return "FillerData" + case NALUTypeSPSExtension: + return "SPSExtension" + case NALUTypePrefix: + return "Prefix" + case NALUTypeSubsetSPS: + return "SubsetSPS" + case NALUTypeReserved16: + return "Reserved16" + case NALUTypeReserved17: + return "Reserved17" + case NALUTypeReserved18: + return "Reserved18" + case NALUTypeSliceLayerWithoutPartitioning: + return "SliceLayerWithoutPartitioning" + case NALUTypeSliceExtension: + return "SliceExtension" + case NALUTypeSliceExtensionDepth: + return "SliceExtensionDepth" + case NALUTypeReserved22: + return "Reserved22" + case NALUTypeReserved23: + return "Reserved23" + case NALUTypeStapA: + return "StapA" + case NALUTypeStapB: + return "StapB" + case NALUTypeMtap16: + return "Mtap16" + case NALUTypeMtap24: + return "Mtap24" + case NALUTypeFuA: + return "FuA" + case NALUTypeFuB: + return "FuB" + } + return "unknown" +} diff --git a/pkg/rtph264/rtph264.go b/pkg/rtph264/rtph264.go index a221ae00..d787a3bd 100644 --- a/pkg/rtph264/rtph264.go +++ b/pkg/rtph264/rtph264.go @@ -10,104 +10,3 @@ type NALUAndTimestamp struct { Timestamp time.Duration NALU []byte } - -// NALUType is the type of a NALU. -type NALUType uint8 - -// standard NALU types. -const ( - NALUTypeNonIDR NALUType = 1 - NALUTypeDataPartitionA NALUType = 2 - NALUTypeDataPartitionB NALUType = 3 - NALUTypeDataPartitionC NALUType = 4 - NALUTypeIDR NALUType = 5 - NALUTypeSei NALUType = 6 - NALUTypeSPS NALUType = 7 - NALUTypePPS NALUType = 8 - NALUTypeAccessUnitDelimiter NALUType = 9 - NALUTypeEndOfSequence NALUType = 10 - NALUTypeEndOfStream NALUType = 11 - NALUTypeFillerData NALUType = 12 - NALUTypeSPSExtension NALUType = 13 - NALUTypePrefix NALUType = 14 - NALUTypeSubsetSPS NALUType = 15 - NALUTypeReserved16 NALUType = 16 - NALUTypeReserved17 NALUType = 17 - NALUTypeReserved18 NALUType = 18 - NALUTypeSliceLayerWithoutPartitioning NALUType = 19 - NALUTypeSliceExtension NALUType = 20 - NALUTypeSliceExtensionDepth NALUType = 21 - NALUTypeReserved22 NALUType = 22 - NALUTypeReserved23 NALUType = 23 - NALUTypeStapA NALUType = 24 - NALUTypeStapB NALUType = 25 - NALUTypeMtap16 NALUType = 26 - NALUTypeMtap24 NALUType = 27 - NALUTypeFuA NALUType = 28 - NALUTypeFuB NALUType = 29 -) - -// String implements fmt.Stringer. -func (nt NALUType) String() string { - switch nt { - case NALUTypeNonIDR: - return "NonIDR" - case NALUTypeDataPartitionA: - return "DataPartitionA" - case NALUTypeDataPartitionB: - return "DataPartitionB" - case NALUTypeDataPartitionC: - return "DataPartitionC" - case NALUTypeIDR: - return "IDR" - case NALUTypeSei: - return "Sei" - case NALUTypeSPS: - return "SPS" - case NALUTypePPS: - return "PPS" - case NALUTypeAccessUnitDelimiter: - return "AccessUnitDelimiter" - case NALUTypeEndOfSequence: - return "EndOfSequence" - case NALUTypeEndOfStream: - return "EndOfStream" - case NALUTypeFillerData: - return "FillerData" - case NALUTypeSPSExtension: - return "SPSExtension" - case NALUTypePrefix: - return "Prefix" - case NALUTypeSubsetSPS: - return "SubsetSPS" - case NALUTypeReserved16: - return "Reserved16" - case NALUTypeReserved17: - return "Reserved17" - case NALUTypeReserved18: - return "Reserved18" - case NALUTypeSliceLayerWithoutPartitioning: - return "SliceLayerWithoutPartitioning" - case NALUTypeSliceExtension: - return "SliceExtension" - case NALUTypeSliceExtensionDepth: - return "SliceExtensionDepth" - case NALUTypeReserved22: - return "Reserved22" - case NALUTypeReserved23: - return "Reserved23" - case NALUTypeStapA: - return "StapA" - case NALUTypeStapB: - return "StapB" - case NALUTypeMtap16: - return "Mtap16" - case NALUTypeMtap24: - return "Mtap24" - case NALUTypeFuA: - return "FuA" - case NALUTypeFuB: - return "FuB" - } - return "unknown" -}