refactor to use a byte slice with bigendian ordering
this matches both the data structure and the underlying data that we receive from the network, so we should also use them internally.
This commit is contained in:
parent
e99b2923c1
commit
88c8e210a6
40
main.go
40
main.go
|
@ -2,8 +2,6 @@ package main
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
|
@ -16,7 +14,7 @@ type (
|
|||
AudioVersionId int
|
||||
LayerIndex int
|
||||
|
||||
header uint32
|
||||
header []byte
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -57,26 +55,26 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
// TODO tests
|
||||
// TODO: tests
|
||||
func (h header) HasSyncWord() bool {
|
||||
return (h>>21)&0x7ff == 0x7ff
|
||||
return h[0] == 0xff && h[1]&0xe0 == 0xe0
|
||||
}
|
||||
|
||||
func (h header) AudioVersionId() AudioVersionId {
|
||||
return AudioVersionId((h >> 19) & 0x3)
|
||||
return AudioVersionId((h[1] >> 3) & 0x3)
|
||||
}
|
||||
|
||||
func (h header) LayerIndex() LayerIndex {
|
||||
return LayerIndex(((h >> 8) & 0x6) >> 1)
|
||||
return LayerIndex((h[1] >> 1) & 0x3)
|
||||
}
|
||||
|
||||
func (h header) IsProtected() bool {
|
||||
return (h>>8)&1 == 1
|
||||
return h[1]&1 == 1
|
||||
}
|
||||
|
||||
func (h header) SampleRate() uint32 {
|
||||
i := h.AudioVersionId()
|
||||
j := (h >> 18) & 0xb11
|
||||
j := (h[2] >> 2) & 0x3
|
||||
return sampleRates[i][j]
|
||||
}
|
||||
|
||||
|
@ -98,6 +96,7 @@ func (h header) IsValid() bool {
|
|||
}
|
||||
|
||||
func (h header) BitRate() uint32 {
|
||||
// TODO: improve this
|
||||
var j int
|
||||
switch h.AudioVersionId() {
|
||||
case AudioVersionMPEG1:
|
||||
|
@ -124,8 +123,7 @@ func (h header) BitRate() uint32 {
|
|||
|
||||
}
|
||||
}
|
||||
k := ((h >> 20) & 0xf)
|
||||
return bitRates[j][k]
|
||||
return bitRates[j][h[2]>>4]
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
@ -147,26 +145,12 @@ func main() {
|
|||
log.Fatal(err)
|
||||
}
|
||||
|
||||
header, err := newHeaderFromBytes(buf)
|
||||
if header.IsValid() {
|
||||
fmt.Printf("%+v\n", buf)
|
||||
fmt.Printf("Got header: %032b, audio version: %d, layer index: %d, bitrate: %d\n", header, header.AudioVersionId(), header.LayerIndex(), header.BitRate())
|
||||
h := header(buf)
|
||||
if h.IsValid() {
|
||||
fmt.Printf("Got header: %08b, audio version: %d, layer index: %d, bitrate: %d\n", h, h.AudioVersionId(), h.LayerIndex(), h.BitRate())
|
||||
} else {
|
||||
bufreader.Discard(3)
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
func newHeaderFromBytes(buf []byte) (header, error) {
|
||||
if len(buf) < 4 {
|
||||
return 0, errors.New("insufficient bytes")
|
||||
}
|
||||
// assume little-endian for now
|
||||
return newHeader(binary.LittleEndian.Uint32(buf))
|
||||
}
|
||||
|
||||
// TODO use unsafe here? https://codereview.stackexchange.com/a/60161
|
||||
func newHeader(n uint32) (header, error) {
|
||||
return header(n), nil
|
||||
}
|
||||
|
|
400
main_test.go
400
main_test.go
|
@ -1,234 +1,226 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAudioVersionId(t *testing.T) {
|
||||
bytes := []byte{0xff, 0xe8, 0, 0}
|
||||
h, err := newHeader(binary.BigEndian.Uint32(bytes))
|
||||
require.NoError(t, err)
|
||||
h := header(bytes)
|
||||
assert.Equal(t, AudioVersionReserved, h.AudioVersionId())
|
||||
|
||||
bytes = []byte{0xff, 0xf0, 0, 0}
|
||||
h, err = newHeader(binary.BigEndian.Uint32(bytes))
|
||||
require.NoError(t, err)
|
||||
h = header(bytes)
|
||||
assert.Equal(t, AudioVersionMPEG2, h.AudioVersionId())
|
||||
|
||||
bytes = []byte{0xff, 0xff, 0, 0}
|
||||
h, err = newHeader(binary.BigEndian.Uint32(bytes))
|
||||
require.NoError(t, err)
|
||||
h = header(bytes)
|
||||
assert.Equal(t, AudioVersionMPEG1, h.AudioVersionId())
|
||||
|
||||
bytes = []byte{0xff, 0xe0, 0, 0}
|
||||
h, err = newHeader(binary.BigEndian.Uint32(bytes))
|
||||
require.NoError(t, err)
|
||||
h = header(bytes)
|
||||
assert.Equal(t, AudioVersionMPEG25, h.AudioVersionId())
|
||||
}
|
||||
|
||||
// func TestLayerIndex(t *testing.T) {
|
||||
// bytes := []byte{0xff, 0xe2, 0, 0}
|
||||
// h, err := newHeader(binary.BigEndian.Uint32(bytes))
|
||||
// require.NoError(t, err)
|
||||
// assert.Equal(t, LayerIndexIII, h.LayerIndex())
|
||||
func TestLayerIndex(t *testing.T) {
|
||||
bytes := []byte{0xff, 0xe0, 0, 0}
|
||||
h := header(bytes)
|
||||
assert.Equal(t, LayerIndexReserved, h.LayerIndex())
|
||||
|
||||
// bytes = []byte{0xff, 0xe4, 0, 0}
|
||||
// h, err = newHeader(binary.BigEndian.Uint32(bytes))
|
||||
// require.NoError(t, err)
|
||||
// assert.Equal(t, LayerIndexII, h.LayerIndex())
|
||||
bytes = []byte{0xff, 0xe2, 0, 0}
|
||||
h = header(bytes)
|
||||
assert.Equal(t, LayerIndexIII, h.LayerIndex())
|
||||
|
||||
// bytes = []byte{0xff, 0xe6, 0, 0}
|
||||
// h, err = newHeader(binary.BigEndian.Uint32(bytes))
|
||||
// require.NoError(t, err)
|
||||
// assert.Equal(t, LayerIndexI, h.LayerIndex())
|
||||
// }
|
||||
bytes = []byte{0xff, 0xe4, 0, 0}
|
||||
h = header(bytes)
|
||||
assert.Equal(t, LayerIndexII, h.LayerIndex())
|
||||
|
||||
// func TestIsProtected(t *testing.T) {
|
||||
// bytes := []byte{0xff, 0xe1, 0, 0}
|
||||
// h, err := newHeader(binary.BigEndian.Uint32(bytes))
|
||||
// require.NoError(t, err)
|
||||
// assert.True(t, h.IsProtected())
|
||||
bytes = []byte{0xff, 0xe6, 0, 0}
|
||||
h = header(bytes)
|
||||
assert.Equal(t, LayerIndexI, h.LayerIndex())
|
||||
}
|
||||
|
||||
// bytes = []byte{0xff, 0xe0, 0, 0}
|
||||
// h, err = newHeader(binary.BigEndian.Uint32(bytes))
|
||||
// require.NoError(t, err)
|
||||
// assert.False(t, h.IsProtected())
|
||||
// }
|
||||
func TestIsProtected(t *testing.T) {
|
||||
bytes := []byte{0xff, 0xe1, 0, 0}
|
||||
h := header(bytes)
|
||||
assert.True(t, h.IsProtected())
|
||||
|
||||
// func TestBitRate(t *testing.T) {
|
||||
// testCases := []struct {
|
||||
// name string
|
||||
// bytes []byte
|
||||
// expBitRate uint32
|
||||
// }{
|
||||
// {
|
||||
// "empty header",
|
||||
// []byte{0, 0, 0, 0},
|
||||
// 0,
|
||||
// },
|
||||
// {
|
||||
// "MPEG1 LayerI 32kbps",
|
||||
// []byte{0xff, 0xfe, 0x10, 0},
|
||||
// 32,
|
||||
// },
|
||||
// {
|
||||
// "MPEG1 LayerI 448kbps",
|
||||
// []byte{0xff, 0xfe, 0xe0, 0},
|
||||
// 448,
|
||||
// },
|
||||
// {
|
||||
// "MPEG1 LayerII 32kbps",
|
||||
// []byte{0xff, 0xfc, 0x10, 0},
|
||||
// 32,
|
||||
// },
|
||||
// {
|
||||
// "MPEG1 LayerII 448kbps",
|
||||
// []byte{0xff, 0xfc, 0xe0, 0},
|
||||
// 384,
|
||||
// },
|
||||
// {
|
||||
// "MPEG1 LayerIII 32kbps",
|
||||
// []byte{0xff, 0xfa, 0x10, 0},
|
||||
// 32,
|
||||
// },
|
||||
// {
|
||||
// "MPEG1 LayerIII 128kbps",
|
||||
// []byte{0xff, 0xfa, 0x90, 0},
|
||||
// 128,
|
||||
// },
|
||||
// {
|
||||
// "MPEG1 LayerIII 160kbps",
|
||||
// []byte{0xff, 0xfa, 0xa0, 0},
|
||||
// 160,
|
||||
// },
|
||||
// {
|
||||
// "MPEG1 LayerIII 192kbps",
|
||||
// []byte{0xff, 0xfb, 0xb0, 0},
|
||||
// 192,
|
||||
// },
|
||||
// {
|
||||
// "MPEG1 LayerIII 320kbps",
|
||||
// []byte{0xff, 0xfa, 0xe0, 0},
|
||||
// 320,
|
||||
// },
|
||||
// {
|
||||
// "MPEG1 LayerIII reserved",
|
||||
// []byte{0xff, 0xfa, 0xf0, 0},
|
||||
// 0,
|
||||
// },
|
||||
// {
|
||||
// "MPEG2 LayerI 32kbps",
|
||||
// []byte{0xff, 0xf6, 0x10, 0},
|
||||
// 32,
|
||||
// },
|
||||
// {
|
||||
// "MPEG2 LayerI 256",
|
||||
// []byte{0xff, 0xf6, 0xe0, 0},
|
||||
// 256,
|
||||
// },
|
||||
// {
|
||||
// "MPEG2 LayerII 32kbps",
|
||||
// []byte{0xff, 0xf4, 0x10, 0},
|
||||
// 8,
|
||||
// },
|
||||
// {
|
||||
// "MPEG2 LayerII 160kbps",
|
||||
// []byte{0xff, 0xf4, 0xe0, 0},
|
||||
// 160,
|
||||
// },
|
||||
// {
|
||||
// "MPEG2 LayerIII 32kbps",
|
||||
// []byte{0xff, 0xf2, 0x10, 0},
|
||||
// 8,
|
||||
// },
|
||||
// {
|
||||
// "MPEG2 LayerIII 160kbps",
|
||||
// []byte{0xff, 0xf2, 0xe0, 0},
|
||||
// 160,
|
||||
// },
|
||||
// {
|
||||
// "MPEG25 LayerI 32kbps",
|
||||
// []byte{0xff, 0xe6, 0x10, 0},
|
||||
// 32,
|
||||
// },
|
||||
// {
|
||||
// "MPEG25 LayerI 256",
|
||||
// []byte{0xff, 0xe6, 0xe0, 0},
|
||||
// 256,
|
||||
// },
|
||||
// {
|
||||
// "MPEG25 LayerII 32kbps",
|
||||
// []byte{0xff, 0xe4, 0x10, 0},
|
||||
// 8,
|
||||
// },
|
||||
// {
|
||||
// "MPEG25 LayerII 160kbps",
|
||||
// []byte{0xff, 0xe4, 0xe0, 0},
|
||||
// 160,
|
||||
// },
|
||||
// {
|
||||
// "MPEG25 LayerIII 32kbps",
|
||||
// []byte{0xff, 0xe2, 0x10, 0},
|
||||
// 8,
|
||||
// },
|
||||
// {
|
||||
// "MPEG25 LayerIII 160kbps",
|
||||
// []byte{0xff, 0xe2, 0xe0, 0},
|
||||
// 160,
|
||||
// },
|
||||
// {
|
||||
// "full header",
|
||||
// []byte{1, 1, 1, 1},
|
||||
// 0,
|
||||
// },
|
||||
// }
|
||||
bytes = []byte{0xff, 0xe0, 0, 0}
|
||||
h = header(bytes)
|
||||
assert.False(t, h.IsProtected())
|
||||
}
|
||||
|
||||
// for _, tc := range testCases {
|
||||
// t.Run(tc.name, func(t *testing.T) {
|
||||
// h, err := newHeader(binary.BigEndian.Uint32(tc.bytes))
|
||||
// require.NoError(t, err)
|
||||
// assert.Equal(t, tc.expBitRate, h.BitRate())
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
func TestBitRate(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
bytes []byte
|
||||
expBitRate uint32
|
||||
}{
|
||||
{
|
||||
"empty header",
|
||||
[]byte{0, 0, 0, 0},
|
||||
0,
|
||||
},
|
||||
{
|
||||
"MPEG1 LayerI 32kbps",
|
||||
[]byte{0xff, 0xfe, 0x10, 0},
|
||||
32,
|
||||
},
|
||||
{
|
||||
"MPEG1 LayerI 448kbps",
|
||||
[]byte{0xff, 0xfe, 0xe0, 0},
|
||||
448,
|
||||
},
|
||||
{
|
||||
"MPEG1 LayerII 32kbps",
|
||||
[]byte{0xff, 0xfc, 0x10, 0},
|
||||
32,
|
||||
},
|
||||
{
|
||||
"MPEG1 LayerII 448kbps",
|
||||
[]byte{0xff, 0xfc, 0xe0, 0},
|
||||
384,
|
||||
},
|
||||
{
|
||||
"MPEG1 LayerIII 32kbps",
|
||||
[]byte{0xff, 0xfa, 0x10, 0},
|
||||
32,
|
||||
},
|
||||
{
|
||||
"MPEG1 LayerIII 128kbps",
|
||||
[]byte{0xff, 0xfa, 0x90, 0},
|
||||
128,
|
||||
},
|
||||
{
|
||||
"MPEG1 LayerIII 160kbps",
|
||||
[]byte{0xff, 0xfa, 0xa0, 0},
|
||||
160,
|
||||
},
|
||||
{
|
||||
"MPEG1 LayerIII 192kbps",
|
||||
[]byte{0xff, 0xfb, 0xb0, 0},
|
||||
192,
|
||||
},
|
||||
{
|
||||
"MPEG1 LayerIII 320kbps",
|
||||
[]byte{0xff, 0xfa, 0xe0, 0},
|
||||
320,
|
||||
},
|
||||
{
|
||||
"MPEG1 LayerIII reserved",
|
||||
[]byte{0xff, 0xfa, 0xf0, 0},
|
||||
0,
|
||||
},
|
||||
{
|
||||
"MPEG2 LayerI 32kbps",
|
||||
[]byte{0xff, 0xf6, 0x10, 0},
|
||||
32,
|
||||
},
|
||||
{
|
||||
"MPEG2 LayerI 256",
|
||||
[]byte{0xff, 0xf6, 0xe0, 0},
|
||||
256,
|
||||
},
|
||||
{
|
||||
"MPEG2 LayerII 32kbps",
|
||||
[]byte{0xff, 0xf4, 0x10, 0},
|
||||
8,
|
||||
},
|
||||
{
|
||||
"MPEG2 LayerII 160kbps",
|
||||
[]byte{0xff, 0xf4, 0xe0, 0},
|
||||
160,
|
||||
},
|
||||
{
|
||||
"MPEG2 LayerIII 32kbps",
|
||||
[]byte{0xff, 0xf2, 0x10, 0},
|
||||
8,
|
||||
},
|
||||
{
|
||||
"MPEG2 LayerIII 160kbps",
|
||||
[]byte{0xff, 0xf2, 0xe0, 0},
|
||||
160,
|
||||
},
|
||||
{
|
||||
"MPEG25 LayerI 32kbps",
|
||||
[]byte{0xff, 0xe6, 0x10, 0},
|
||||
32,
|
||||
},
|
||||
{
|
||||
"MPEG25 LayerI 256",
|
||||
[]byte{0xff, 0xe6, 0xe0, 0},
|
||||
256,
|
||||
},
|
||||
{
|
||||
"MPEG25 LayerII 32kbps",
|
||||
[]byte{0xff, 0xe4, 0x10, 0},
|
||||
8,
|
||||
},
|
||||
{
|
||||
"MPEG25 LayerII 160kbps",
|
||||
[]byte{0xff, 0xe4, 0xe0, 0},
|
||||
160,
|
||||
},
|
||||
{
|
||||
"MPEG25 LayerIII 32kbps",
|
||||
[]byte{0xff, 0xe2, 0x10, 0},
|
||||
8,
|
||||
},
|
||||
{
|
||||
"MPEG25 LayerIII 160kbps",
|
||||
[]byte{0xff, 0xe2, 0xe0, 0},
|
||||
160,
|
||||
},
|
||||
{
|
||||
"full header",
|
||||
[]byte{1, 1, 1, 1},
|
||||
0,
|
||||
},
|
||||
}
|
||||
|
||||
// func TestSampleRate(t *testing.T) {
|
||||
// testCases := []struct {
|
||||
// name string
|
||||
// bytes []byte
|
||||
// expSampleRate uint32
|
||||
// }{
|
||||
// {
|
||||
// "MPEG1 44.1k",
|
||||
// []byte{0xff, 0xfa, 0x90, 0},
|
||||
// 44100,
|
||||
// },
|
||||
// {
|
||||
// "MPEG1 48k",
|
||||
// []byte{0xff, 0xfa, 0x94, 0},
|
||||
// 48000,
|
||||
// },
|
||||
// {
|
||||
// "MPEG1 32k",
|
||||
// []byte{0xff, 0xfa, 0x98, 0},
|
||||
// 32000,
|
||||
// },
|
||||
// {
|
||||
// "MPEG1 Reserved",
|
||||
// []byte{0xff, 0xfa, 0x9c, 0},
|
||||
// 32000,
|
||||
// },
|
||||
// }
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
h := header(tc.bytes)
|
||||
assert.Equal(t, tc.expBitRate, h.BitRate())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// for _, tc := range testCases {
|
||||
// t.Run(tc.name, func(t *testing.T) {
|
||||
// h, err := newHeader(binary.BigEndian.Uint32(tc.bytes))
|
||||
// require.NoError(t, err)
|
||||
// assert.Equal(t, int(tc.expSampleRate), int(h.SampleRate()))
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
func TestSampleRate(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
bytes []byte
|
||||
expSampleRate uint32
|
||||
}{
|
||||
{
|
||||
"MPEG1 44.1k",
|
||||
[]byte{0xff, 0xfa, 0x90, 0},
|
||||
44100,
|
||||
},
|
||||
{
|
||||
"MPEG1 48k",
|
||||
[]byte{0xff, 0xfa, 0x94, 0},
|
||||
48000,
|
||||
},
|
||||
{
|
||||
"MPEG1 32k",
|
||||
[]byte{0xff, 0xfa, 0x98, 0},
|
||||
32000,
|
||||
},
|
||||
{
|
||||
"MPEG1 Reserved",
|
||||
[]byte{0xff, 0xfa, 0x9c, 0},
|
||||
0,
|
||||
},
|
||||
// TODO finish
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
h := header(tc.bytes)
|
||||
assert.Equal(t, int(tc.expSampleRate), int(h.SampleRate()))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue