Pure go FLAC packetizer doing minimal stream decoding, allow offseting packets.
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This removes the libflac / flacgo packetizers
This commit is contained in:
parent
041e51b106
commit
4073f8f0d7
22
README.md
22
README.md
|
@ -27,15 +27,15 @@ Collection of audio utilities for decoding/encoding/processing files and streams
|
|||
|
||||
Only output from Kirika's own encoders is supported.
|
||||
|
||||
| Container | Packetizer | Keep Mode | Sample Numbers | Offset | Notes |
|
||||
|:---------:|:----------:|:---------:|:--------------:|:------:|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **FLAC** | ✅ | ✅ | ✅ | ❌ | Uses [libFLAC](https://github.com/xiph/flac) via [goflac](https://git.gammaspectra.live/S.O.N.G/goflac) for parsing streams.<br/>If not available, [flacgo](https://git.gammaspectra.live/S.O.N.G/flacgo) will be used. |
|
||||
| **TTA** | ❌ | - | - | - | |
|
||||
| **MP3** | ✅ | ✅ | ✅ | ✅ | Uses [sssgun/mp3](https://github.com/sssgun/mp3) as a frame parser. |
|
||||
| **Ogg** | ✅ | ✅ | ✅* | ✅ | *Sample numbers (absolute granule position in Ogg) depend on underlying codec implementing it.<br/>Has been tested as working for Opus |
|
||||
| **ADTS** | ✅ | ✅ | ✅ | ✅ | Uses [edgeware/mp4ff](https://github.com/edgeware/mp4ff) for its ADTS frame parser. |
|
||||
| **MP4** | ❌ | - | - | - | |
|
||||
| **ADIF** | ❌ | - | - | - | |
|
||||
| Container | Packetizer | Keep Mode | Sample Numbers | Offset | Notes |
|
||||
|:---------:|:----------:|:---------:|:--------------:|:------:|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **FLAC** | ✅ | ✅ | ✅ | ✅ | Uses custom Kirika packetizer. |
|
||||
| **TTA** | ❌ | - | - | - | |
|
||||
| **MP3** | ✅ | ✅ | ✅ | ✅ | Uses [sssgun/mp3](https://github.com/sssgun/mp3) as a frame parser. |
|
||||
| **Ogg** | ✅ | ✅ | ✅* | ✅ | Uses custom Kirika packetizer.<br/>*Sample numbers (absolute granule position in Ogg) depend on underlying codec implementing it.<br/>Has been tested as working for Opus |
|
||||
| **ADTS** | ✅ | ✅ | ✅ | ✅ | Uses [edgeware/mp4ff](https://github.com/edgeware/mp4ff) for its ADTS frame parser. |
|
||||
| **MP4** | ❌ | - | - | - | |
|
||||
| **ADIF** | ❌ | - | - | - | |
|
||||
|
||||
## Dependencies
|
||||
### Go >= 1.18
|
||||
|
@ -136,9 +136,9 @@ If this tag is enabled, yet `aac` support remains enabled, an AAC encoder (but n
|
|||
|
||||
|
||||
### disable_codec_libflac
|
||||
This tag disables the [libFLAC](https://gitlab.xiph.org/xiph/flac) support for decoding/encoding/packetizing FLAC.
|
||||
This tag disables the [libFLAC](https://gitlab.xiph.org/xiph/flac) support for decoding/encoding FLAC.
|
||||
|
||||
If this tag is enabled, yet `flac` support remains enabled, [flacgo](https://git.gammaspectra.live/S.O.N.G/flacgo) FLAC decoder (but not encoder) will be used.
|
||||
If this tag is enabled, yet `flac` support remains enabled, [flacgo](https://git.gammaspectra.live/S.O.N.G/flacgo) FLAC decoder/encoder will be used.
|
||||
|
||||
### disable_codec_lame
|
||||
This tag disables the [LAME](https://github.com/viert/go-lame) support for encoding MP3. This is available for specific problems with the LGPL v2 license that conflicts with your needs or policy.
|
||||
|
|
|
@ -6,13 +6,11 @@ import (
|
|||
"bytes"
|
||||
"errors"
|
||||
"git.gammaspectra.live/S.O.N.G/Kirika/audio"
|
||||
"git.gammaspectra.live/S.O.N.G/Kirika/audio/filter"
|
||||
"git.gammaspectra.live/S.O.N.G/Kirika/audio/format"
|
||||
libflac "git.gammaspectra.live/S.O.N.G/flacgo"
|
||||
"git.gammaspectra.live/S.O.N.G/flacgo/frame"
|
||||
"git.gammaspectra.live/S.O.N.G/flacgo/meta"
|
||||
"io"
|
||||
"log"
|
||||
)
|
||||
|
||||
type Format struct {
|
||||
|
@ -156,31 +154,61 @@ func (f Format) Encode(source audio.Source, writer io.WriteCloser, options map[s
|
|||
}
|
||||
}
|
||||
|
||||
for block := range filter.NewBlockSizeFilter(int(blockSize)).Process(source).ToInt32(int(bitsPerSample)).GetBlocks() {
|
||||
buf := make([]int32, 0, blockSize)
|
||||
|
||||
minBlockSize := len(block) / channels.Count()
|
||||
if uint16(minBlockSize) > blockSize {
|
||||
log.Panicf("invalid block size %d > %d", minBlockSize, blockSize)
|
||||
//Uses verbatim samples + constant. No savings will be had, but it's a proper FLAC
|
||||
|
||||
for block := range source.ToInt32(int(bitsPerSample)).GetBlocks() {
|
||||
|
||||
buf = append(buf, block...)
|
||||
|
||||
for len(buf) >= int(blockSize)*ccount {
|
||||
|
||||
for i := 0; i < ccount; i++ {
|
||||
//De-interleave samples
|
||||
for j := 0; j < int(blockSize); j++ {
|
||||
subframes[i].Samples[j] = buf[j*ccount+i]
|
||||
}
|
||||
}
|
||||
|
||||
err = encoder.WriteFrame(&frame.Frame{
|
||||
Header: frame.Header{
|
||||
HasFixedBlockSize: true,
|
||||
BlockSize: blockSize,
|
||||
SampleRate: sampleRate,
|
||||
Channels: channels,
|
||||
BitsPerSample: bitsPerSample,
|
||||
},
|
||||
Subframes: subframes,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf = buf[int(blockSize)*ccount:]
|
||||
}
|
||||
|
||||
for i := 0; i < ccount; i++ {
|
||||
//Uses verbatim samples + constant. No savings will be had, but it's a proper FLAC
|
||||
if len(buf) == 0 {
|
||||
//free
|
||||
buf = nil
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: blockSize will pad the end, is it allowed to make it smaller?
|
||||
if len(buf) > 0 {
|
||||
for i := 0; i < ccount; i++ {
|
||||
//De-interleave samples
|
||||
for j := 0; j < minBlockSize; j++ {
|
||||
subframes[i].Samples[j] = block[j*ccount+i]
|
||||
}
|
||||
//zero remaining, if any
|
||||
for j := uint16(minBlockSize); j < blockSize; j++ {
|
||||
subframes[i].Samples[j] = 0
|
||||
for j := 0; j < len(buf)/ccount; j++ {
|
||||
subframes[i].Samples[j] = buf[j*ccount+i]
|
||||
}
|
||||
subframes[i].Samples = subframes[i].Samples[0 : len(buf)/ccount]
|
||||
subframes[i].NSamples = len(subframes[i].Samples)
|
||||
}
|
||||
|
||||
err = encoder.WriteFrame(&frame.Frame{
|
||||
Header: frame.Header{
|
||||
HasFixedBlockSize: true,
|
||||
BlockSize: blockSize,
|
||||
BlockSize: uint16(subframes[0].NSamples),
|
||||
SampleRate: sampleRate,
|
||||
Channels: channels,
|
||||
BitsPerSample: bitsPerSample,
|
||||
|
|
|
@ -2,14 +2,651 @@ package packetizer
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/icza/bitio"
|
||||
"io"
|
||||
)
|
||||
|
||||
func NewFLACPacketizer(reader io.Reader) *FLACPacketizer {
|
||||
return &FLACPacketizer{br: bitio.NewReader(bufio.NewReaderSize(reader, 4096))}
|
||||
}
|
||||
|
||||
type FLACHeaderPacket struct {
|
||||
MinimumBlockSize uint16
|
||||
MaximumBlockSize uint16
|
||||
MinimumFrameSize uint32
|
||||
MaximumFrameSize uint32
|
||||
SampleRate uint32
|
||||
ChannelCount uint8
|
||||
BitsPerSample uint8
|
||||
TotalSamples uint64
|
||||
Hash [128 / 8]byte
|
||||
MetadataBlocks [][]byte
|
||||
buf []byte
|
||||
}
|
||||
|
||||
func (p *FLACHeaderPacket) KeepMode() KeepMode {
|
||||
return Keep
|
||||
}
|
||||
|
||||
func (p *FLACHeaderPacket) GetStartSampleNumber() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (p *FLACHeaderPacket) GetEndSampleNumber() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (p *FLACHeaderPacket) GetData() []byte {
|
||||
data := bytes.NewBuffer([]byte{})
|
||||
buf := bitio.NewWriter(data)
|
||||
if _, err := buf.Write([]byte("fLaC")); err != nil {
|
||||
return nil
|
||||
}
|
||||
for _, b := range p.MetadataBlocks {
|
||||
if buf.WriteByte(b[0]) != nil {
|
||||
return nil
|
||||
}
|
||||
if err := buf.WriteBits(uint64(len(b)-1), 24); err != nil {
|
||||
return nil
|
||||
}
|
||||
if _, err := buf.Write(b[1:]); err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
buf.Align()
|
||||
|
||||
return data.Bytes()
|
||||
}
|
||||
|
||||
func (p *FLACHeaderPacket) Category() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
const invalidMetadataBlock = 127
|
||||
|
||||
func readFlacMetadataBlock(br *bitio.Reader) (block byte, data []byte) {
|
||||
var err error
|
||||
if block, err = br.ReadByte(); err != nil {
|
||||
return invalidMetadataBlock, nil
|
||||
}
|
||||
var lengthInBytes uint64
|
||||
if lengthInBytes, err = br.ReadBits(24); err != nil {
|
||||
return invalidMetadataBlock, nil
|
||||
}
|
||||
data = make([]byte, lengthInBytes)
|
||||
if _, err = io.ReadFull(br, data); err != nil {
|
||||
return invalidMetadataBlock, nil
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func readFlacHeader(br *bitio.Reader) *FLACHeaderPacket {
|
||||
if n, err := br.ReadBits(32); err != nil || n != 0x664C6143 {
|
||||
return nil
|
||||
}
|
||||
|
||||
block, data := readFlacMetadataBlock(br)
|
||||
if (block&0x7F) != 0 || len(data) < ((16+16+24+24+20+3+5+36+128)/8) { //StreamInfo
|
||||
return nil
|
||||
}
|
||||
hdr := &FLACHeaderPacket{
|
||||
buf: make([]byte, 0, 4096),
|
||||
}
|
||||
|
||||
hdr.MetadataBlocks = append(hdr.MetadataBlocks, append([]byte{block}, data...))
|
||||
|
||||
sbr := bitio.NewReader(bytes.NewBuffer(data))
|
||||
|
||||
var n uint64
|
||||
|
||||
n, _ = sbr.ReadBits(16)
|
||||
hdr.MinimumBlockSize = uint16(n)
|
||||
n, _ = sbr.ReadBits(16)
|
||||
hdr.MaximumBlockSize = uint16(n)
|
||||
n, _ = sbr.ReadBits(24)
|
||||
hdr.MinimumFrameSize = uint32(n)
|
||||
n, _ = sbr.ReadBits(24)
|
||||
hdr.MaximumFrameSize = uint32(n)
|
||||
n, _ = sbr.ReadBits(20)
|
||||
hdr.SampleRate = uint32(n)
|
||||
n, _ = sbr.ReadBits(3)
|
||||
hdr.ChannelCount = uint8(n) + 1
|
||||
n, _ = sbr.ReadBits(5)
|
||||
hdr.BitsPerSample = uint8(n) + 1
|
||||
_, _ = sbr.Read(hdr.Hash[:])
|
||||
|
||||
for {
|
||||
if (block & (1 << 7)) > 0 { //if last
|
||||
break
|
||||
}
|
||||
block, data = readFlacMetadataBlock(br)
|
||||
if block == invalidMetadataBlock {
|
||||
return nil
|
||||
}
|
||||
|
||||
hdr.MetadataBlocks = append(hdr.MetadataBlocks, append([]byte{block}, data...))
|
||||
}
|
||||
|
||||
return hdr
|
||||
}
|
||||
|
||||
func readFlacFrame(hdr *FLACHeaderPacket, br *bitio.Reader) *FLACPacket {
|
||||
if sync, err := br.ReadBits(14); err != nil || sync != 0b11111111111110 {
|
||||
return nil
|
||||
}
|
||||
if reserved, err := br.ReadBits(1); err != nil || reserved != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
p := &FLACPacket{
|
||||
hdr: hdr,
|
||||
mode: Discard,
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
if p.BlockingStrategy, err = br.ReadBits(1); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if p.BlockSizeRaw, err = br.ReadBits(4); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if p.SampleRateRaw, err = br.ReadBits(4); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if p.ChannelAssignment, err = br.ReadBits(4); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if p.BitsPerSample, err = br.ReadBits(3); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if reserved, err := br.ReadBits(1); err != nil || reserved != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if p.Number, err = func(r io.ByteReader) (x uint64, err error) {
|
||||
const (
|
||||
tx = 0x80 // 1000 0000
|
||||
t2 = 0xC0 // 1100 0000
|
||||
t3 = 0xE0 // 1110 0000
|
||||
t4 = 0xF0 // 1111 0000
|
||||
t5 = 0xF8 // 1111 1000
|
||||
t6 = 0xFC // 1111 1100
|
||||
t7 = 0xFE // 1111 1110
|
||||
t8 = 0xFF // 1111 1111
|
||||
|
||||
maskx = 0x3F // 0011 1111
|
||||
mask2 = 0x1F // 0001 1111
|
||||
mask3 = 0x0F // 0000 1111
|
||||
mask4 = 0x07 // 0000 0111
|
||||
mask5 = 0x03 // 0000 0011
|
||||
mask6 = 0x01 // 0000 0001
|
||||
|
||||
rune1Max = 1<<7 - 1
|
||||
rune2Max = 1<<11 - 1
|
||||
rune3Max = 1<<16 - 1
|
||||
rune4Max = 1<<21 - 1
|
||||
rune5Max = 1<<26 - 1
|
||||
rune6Max = 1<<31 - 1
|
||||
rune7Max = 1<<36 - 1
|
||||
)
|
||||
|
||||
c0, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// 1-byte, 7-bit sequence?
|
||||
if c0 < tx {
|
||||
// if c0 == 0xxxxxxx
|
||||
// total: 7 bits (7)
|
||||
return uint64(c0), nil
|
||||
}
|
||||
|
||||
// unexpected continuation byte?
|
||||
if c0 < t2 {
|
||||
// if c0 == 10xxxxxx
|
||||
return 0, errors.New("unexpected continuation byte")
|
||||
}
|
||||
|
||||
// get number of continuation bytes and store bits from c0.
|
||||
var l int
|
||||
switch {
|
||||
case c0 < t3:
|
||||
// if c0 == 110xxxxx
|
||||
// total: 11 bits (5 + 6)
|
||||
l = 1
|
||||
x = uint64(c0 & mask2)
|
||||
case c0 < t4:
|
||||
// if c0 == 1110xxxx
|
||||
// total: 16 bits (4 + 6 + 6)
|
||||
l = 2
|
||||
x = uint64(c0 & mask3)
|
||||
case c0 < t5:
|
||||
// if c0 == 11110xxx
|
||||
// total: 21 bits (3 + 6 + 6 + 6)
|
||||
l = 3
|
||||
x = uint64(c0 & mask4)
|
||||
case c0 < t6:
|
||||
// if c0 == 111110xx
|
||||
// total: 26 bits (2 + 6 + 6 + 6 + 6)
|
||||
l = 4
|
||||
x = uint64(c0 & mask5)
|
||||
case c0 < t7:
|
||||
// if c0 == 1111110x
|
||||
// total: 31 bits (1 + 6 + 6 + 6 + 6 + 6)
|
||||
l = 5
|
||||
x = uint64(c0 & mask6)
|
||||
case c0 < t8:
|
||||
// if c0 == 11111110
|
||||
// total: 36 bits (0 + 6 + 6 + 6 + 6 + 6 + 6)
|
||||
l = 6
|
||||
x = 0
|
||||
}
|
||||
|
||||
// store bits from continuation bytes.
|
||||
for i := 0; i < l; i++ {
|
||||
x <<= 6
|
||||
c, err := r.ReadByte()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
if c < tx || t2 <= c {
|
||||
// if c != 10xxxxxx
|
||||
return 0, errors.New("expected continuation byte")
|
||||
}
|
||||
x |= uint64(c & maskx)
|
||||
}
|
||||
|
||||
// check if number representation is larger than necessary.
|
||||
switch l {
|
||||
case 1:
|
||||
if x <= rune1Max {
|
||||
return 0, fmt.Errorf("larger number representation than necessary; x (%d) stored in %d bytes, could be stored in %d bytes", x, l+1, l)
|
||||
}
|
||||
case 2:
|
||||
if x <= rune2Max {
|
||||
return 0, fmt.Errorf("larger number representation than necessary; x (%d) stored in %d bytes, could be stored in %d bytes", x, l+1, l)
|
||||
}
|
||||
case 3:
|
||||
if x <= rune3Max {
|
||||
return 0, fmt.Errorf("larger number representation than necessary; x (%d) stored in %d bytes, could be stored in %d bytes", x, l+1, l)
|
||||
}
|
||||
case 4:
|
||||
if x <= rune4Max {
|
||||
return 0, fmt.Errorf("larger number representation than necessary; x (%d) stored in %d bytes, could be stored in %d bytes", x, l+1, l)
|
||||
}
|
||||
case 5:
|
||||
if x <= rune5Max {
|
||||
return 0, fmt.Errorf("larger number representation than necessary; x (%d) stored in %d bytes, could be stored in %d bytes", x, l+1, l)
|
||||
}
|
||||
case 6:
|
||||
if x <= rune6Max {
|
||||
return 0, fmt.Errorf("larger number representation than necessary; x (%d) stored in %d bytes, could be stored in %d bytes", x, l+1, l)
|
||||
}
|
||||
}
|
||||
return x, nil
|
||||
}(br); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if p.BlockSizeRaw == 0x6 {
|
||||
if p.BlockSizeExtra, err = br.ReadBits(8); err != nil {
|
||||
return nil
|
||||
}
|
||||
} else if p.BlockSizeRaw == 0x7 {
|
||||
if p.BlockSizeExtra, err = br.ReadBits(16); err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if p.SampleRateRaw == 0xC {
|
||||
if p.SampleRateExtra, err = br.ReadBits(8); err != nil {
|
||||
return nil
|
||||
}
|
||||
} else if p.SampleRateRaw == 0xD || p.SampleRateRaw == 0xE {
|
||||
if p.SampleRateExtra, err = br.ReadBits(16); err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if p.CRC8, err = br.ReadBits(8); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
channelCount := int(p.ChannelAssignment) + 1
|
||||
if p.ChannelAssignment == 0x8 || p.ChannelAssignment == 0x9 || p.ChannelAssignment == 0xA {
|
||||
channelCount = 2
|
||||
}
|
||||
|
||||
blockSize := int(p.getBlockSize())
|
||||
|
||||
buf := bytes.NewBuffer(hdr.buf[:0])
|
||||
|
||||
bw := bitio.NewWriter(buf)
|
||||
|
||||
var padding, subframeType, wastedBitsFlag, wastedBits uint64
|
||||
//TODO: remove error checking here, given we get proper input and a check will happen later
|
||||
for channelNumber := 0; channelNumber < channelCount; channelNumber++ {
|
||||
bps := p.getBitsPerSample()
|
||||
|
||||
if p.ChannelAssignment == 0x9 && channelNumber == 0 {
|
||||
bps++
|
||||
} else if (p.ChannelAssignment == 0x8 || p.ChannelAssignment == 0xA) && channelNumber == 1 {
|
||||
bps++
|
||||
}
|
||||
|
||||
if padding, err = br.ReadBits(1); err != nil {
|
||||
return nil
|
||||
}
|
||||
if subframeType, err = br.ReadBits(6); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if wastedBitsFlag, err = br.ReadBits(1); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if wastedBitsFlag == 1 {
|
||||
if wastedBits, err = readUnary(br); err != nil {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
wastedBits = 0
|
||||
}
|
||||
|
||||
_ = bw.WriteBits(padding, 1)
|
||||
_ = bw.WriteBits(subframeType, 6)
|
||||
_ = bw.WriteBits(wastedBitsFlag, 1)
|
||||
if wastedBitsFlag == 1 {
|
||||
_ = writeUnary(bw, wastedBits)
|
||||
|
||||
bps -= uint8(wastedBits) + 1
|
||||
}
|
||||
|
||||
switch {
|
||||
case subframeType == 0: //Constant
|
||||
|
||||
var constantSample uint64
|
||||
if constantSample, err = br.ReadBits(bps); err != nil {
|
||||
return nil
|
||||
}
|
||||
_ = bw.WriteBits(constantSample, bps)
|
||||
case subframeType == 1: //Verbatim
|
||||
var verbatimData []byte
|
||||
if verbatimData, err = readManyBits(br, blockSize*int(bps)); err != nil {
|
||||
return nil
|
||||
}
|
||||
_ = writeManyBits(bw, verbatimData, blockSize*int(bps))
|
||||
case subframeType < 8: //reserved
|
||||
return nil
|
||||
case subframeType < 16: //Fixed
|
||||
order := int(subframeType & 0x07)
|
||||
|
||||
if order > 4 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var warmupData []byte
|
||||
if warmupData, err = readManyBits(br, order*int(bps)); err != nil {
|
||||
return nil
|
||||
}
|
||||
_ = writeManyBits(bw, warmupData, order*int(bps))
|
||||
|
||||
if err = decodeResidual(br, bw, blockSize, order); err != nil {
|
||||
return nil
|
||||
}
|
||||
case subframeType < 32: //reserved
|
||||
return nil
|
||||
default: //FIR
|
||||
order := int(subframeType&0x1F) + 1
|
||||
var warmupData []byte
|
||||
if warmupData, err = readManyBits(br, order*int(bps)); err != nil {
|
||||
return nil
|
||||
}
|
||||
_ = writeManyBits(bw, warmupData, order*int(bps))
|
||||
|
||||
var coefficientPrecisionLen, quantizationLevel uint64
|
||||
if coefficientPrecisionLen, err = br.ReadBits(4); err != nil {
|
||||
return nil
|
||||
}
|
||||
_ = bw.WriteBits(coefficientPrecisionLen, 4)
|
||||
|
||||
if coefficientPrecisionLen == 0xF {
|
||||
return nil
|
||||
}
|
||||
|
||||
precision := int(coefficientPrecisionLen) + 1
|
||||
|
||||
if quantizationLevel, err = br.ReadBits(5); err != nil {
|
||||
return nil
|
||||
}
|
||||
_ = bw.WriteBits(quantizationLevel, 5)
|
||||
|
||||
var coefficientData []byte
|
||||
if coefficientData, err = readManyBits(br, order*precision); err != nil {
|
||||
return nil
|
||||
}
|
||||
_ = writeManyBits(bw, coefficientData, order*precision)
|
||||
|
||||
if err = decodeResidual(br, bw, blockSize, order); err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if n := br.Align(); n > 0 {
|
||||
_ = bw.WriteBits(0, n)
|
||||
if n, err = bw.Align(); err != nil || n != 0 {
|
||||
//Alignment mismatch!
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
p.FrameData = buf.Bytes()
|
||||
|
||||
if p.CRC16, err = br.ReadBits(16); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func decodeResidual(br *bitio.Reader, bw *bitio.Writer, blockSize, predictorOrder int) (err error) {
|
||||
var entropyCodingMethod, partitionOrder uint64
|
||||
if entropyCodingMethod, err = br.ReadBits(2); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = bw.WriteBits(entropyCodingMethod, 2)
|
||||
|
||||
paramSize := 0
|
||||
|
||||
switch entropyCodingMethod {
|
||||
case 0x0:
|
||||
paramSize = 4
|
||||
case 0x1:
|
||||
paramSize = 5
|
||||
default:
|
||||
return errors.New("wrong method")
|
||||
}
|
||||
|
||||
if partitionOrder, err = br.ReadBits(4); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = bw.WriteBits(partitionOrder, 4)
|
||||
|
||||
partitions := 1 << partitionOrder
|
||||
|
||||
var riceParameter uint64
|
||||
for partition := 0; partition < partitions; partition++ {
|
||||
if riceParameter, err = br.ReadBits(uint8(paramSize)); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = bw.WriteBits(riceParameter, uint8(paramSize))
|
||||
|
||||
var nsamples int
|
||||
if partition != 0 {
|
||||
nsamples = blockSize / partitions
|
||||
} else {
|
||||
nsamples = blockSize/partitions - predictorOrder
|
||||
}
|
||||
|
||||
if (paramSize == 4 && riceParameter == 0xF) || (paramSize == 5 && riceParameter == 0x1F) {
|
||||
// 1111 or 11111: Escape code, meaning the partition is in unencoded
|
||||
// binary form using riceParameter2 bits per sample; riceParameter2 follows as a 5-bit number.
|
||||
var riceParameter2 uint64
|
||||
|
||||
if riceParameter2, err = br.ReadBits(5); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = bw.WriteBits(riceParameter2, 5)
|
||||
|
||||
if riceParameter2 > 0 {
|
||||
var sampleData []byte
|
||||
if sampleData, err = readManyBits(br, nsamples*int(riceParameter2)); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = writeManyBits(bw, sampleData, nsamples*int(riceParameter2))
|
||||
}
|
||||
} else {
|
||||
|
||||
//TODO: optimize this!
|
||||
var high, low uint64
|
||||
if riceParameter == 0 {
|
||||
for j := 0; j < nsamples; j++ {
|
||||
if high, err = readUnary(br); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = writeUnary(bw, high)
|
||||
}
|
||||
} else {
|
||||
for j := 0; j < nsamples; j++ {
|
||||
if high, err = readUnary(br); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = writeUnary(bw, high)
|
||||
if low, err = br.ReadBits(uint8(riceParameter)); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = bw.WriteBits(low, uint8(riceParameter))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeRicePart(br *bitio.Reader, paramSize int) {
|
||||
|
||||
}
|
||||
|
||||
func readManyBits(br *bitio.Reader, bits int) (data []byte, err error) {
|
||||
dataLen := bits / 8
|
||||
if (bits % 8) != 0 {
|
||||
dataLen++
|
||||
}
|
||||
|
||||
data = make([]byte, dataLen)
|
||||
for i := 0; i < (bits / 8); i++ {
|
||||
if data[i], err = br.ReadByte(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if (bits % 8) != 0 {
|
||||
var b uint64
|
||||
if b, err = br.ReadBits(uint8(bits % 8)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data[len(data)-1] = byte(b)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func writeManyBits(bw *bitio.Writer, data []byte, bits int) (err error) {
|
||||
for i := 0; i < (bits / 8); i++ {
|
||||
if err = bw.WriteByte(data[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if (bits % 8) != 0 {
|
||||
if err = bw.WriteBits(uint64(data[(bits/8)]), uint8(bits%8)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func readUnary(br *bitio.Reader) (x uint64, err error) {
|
||||
for {
|
||||
bit, err := br.ReadBits(1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if bit == 1 {
|
||||
break
|
||||
}
|
||||
x++
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// WriteUnary encodes x as an unary coded integer, whose value is represented by
|
||||
// the number of leading zeros before a one.
|
||||
//
|
||||
// Examples of unary coded binary on the left and decoded decimal on the right:
|
||||
//
|
||||
// 0 => 1
|
||||
// 1 => 01
|
||||
// 2 => 001
|
||||
// 3 => 0001
|
||||
// 4 => 00001
|
||||
// 5 => 000001
|
||||
// 6 => 0000001
|
||||
func writeUnary(bw *bitio.Writer, x uint64) error {
|
||||
bits := uint64(1)
|
||||
n := byte(1)
|
||||
for ; x > 0; x-- {
|
||||
n++
|
||||
}
|
||||
if err := bw.WriteBits(bits, n); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type FLACPacket struct {
|
||||
mode KeepMode
|
||||
sampleNumber int64
|
||||
endSampleNumber int64
|
||||
data []byte
|
||||
mode KeepMode
|
||||
hdr *FLACHeaderPacket
|
||||
|
||||
BlockingStrategy uint64
|
||||
BlockSizeRaw uint64
|
||||
SampleRateRaw uint64
|
||||
ChannelAssignment uint64
|
||||
BitsPerSample uint64
|
||||
Number uint64
|
||||
BlockSizeExtra uint64
|
||||
SampleRateExtra uint64
|
||||
CRC8 uint64
|
||||
|
||||
FrameData []byte
|
||||
|
||||
CRC16 uint64
|
||||
}
|
||||
|
||||
func (p *FLACPacket) KeepMode() KeepMode {
|
||||
|
@ -17,48 +654,310 @@ func (p *FLACPacket) KeepMode() KeepMode {
|
|||
}
|
||||
|
||||
func (p *FLACPacket) GetStartSampleNumber() int64 {
|
||||
return p.sampleNumber
|
||||
if p.BlockingStrategy == 0 { //fixed-blocksize
|
||||
return int64(p.Number * uint64(p.getBlockSize()))
|
||||
}
|
||||
return int64(p.Number)
|
||||
}
|
||||
|
||||
func (p *FLACPacket) GetEndSampleNumber() int64 {
|
||||
return p.endSampleNumber
|
||||
return p.GetStartSampleNumber() + int64(p.getBlockSize())
|
||||
}
|
||||
|
||||
func (p *FLACPacket) GetData() []byte {
|
||||
return p.data
|
||||
return p.GetDataOffset(0)
|
||||
}
|
||||
|
||||
func (p *FLACPacket) Category() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func NewFLACPacketizer(reader io.Reader) *FLACPacketizer {
|
||||
return &FLACPacketizer{reader: bufio.NewReader(reader)}
|
||||
}
|
||||
func (p *FLACPacket) getBlockSize() uint16 {
|
||||
n := p.BlockSizeRaw
|
||||
x := p.BlockSizeExtra
|
||||
|
||||
func (o *FLACPacketizer) Read(p []byte) (n int, err error) {
|
||||
n, err = o.reader.Read(p)
|
||||
if n > 0 {
|
||||
o.buffer = append(o.buffer, p[:n]...)
|
||||
switch {
|
||||
case n == 0x0:
|
||||
// 0000: reserved.
|
||||
return 0
|
||||
case n == 0x1:
|
||||
// 0001: 192 samples.
|
||||
return 192
|
||||
case n >= 0x2 && n <= 0x5:
|
||||
// 0010-0101: 576 * 2^(n-2) samples.
|
||||
return 576 * (1 << (n - 2))
|
||||
case n == 0x6:
|
||||
// 0110: get 8 bit (block size)-1 from the end of the header.
|
||||
return uint16(x + 1)
|
||||
case n == 0x7:
|
||||
// 0111: get 16 bit (block size)-1 from the end of the header.
|
||||
return uint16(x + 1)
|
||||
default:
|
||||
// 1000-1111: 256 * 2^(n-8) samples.
|
||||
return 256 * (1 << (n - 8))
|
||||
}
|
||||
o.bytesRead += int64(n)
|
||||
return
|
||||
}
|
||||
|
||||
func (o *FLACPacketizer) Seek(offset int64, whence int) (int64, error) {
|
||||
if offset == 0 && whence == io.SeekCurrent {
|
||||
return o.bytesRead, nil
|
||||
func (p *FLACPacket) getSampleRate() uint32 {
|
||||
switch p.SampleRateRaw {
|
||||
case 0x0:
|
||||
// 0000: unknown sample rate; get from StreamInfo.
|
||||
return p.hdr.SampleRate
|
||||
case 0x1:
|
||||
// 0001: 88.2 kHz.
|
||||
return 88200
|
||||
case 0x2:
|
||||
// 0010: 176.4 kHz.
|
||||
return 176400
|
||||
case 0x3:
|
||||
// 0011: 192 kHz.
|
||||
return 192000
|
||||
case 0x4:
|
||||
// 0100: 8 kHz.
|
||||
return 8000
|
||||
case 0x5:
|
||||
// 0101: 16 kHz.
|
||||
return 16000
|
||||
case 0x6:
|
||||
// 0110: 22.05 kHz.
|
||||
return 22050
|
||||
case 0x7:
|
||||
// 0111: 24 kHz.
|
||||
return 24000
|
||||
case 0x8:
|
||||
// 1000: 32 kHz.
|
||||
return 32000
|
||||
case 0x9:
|
||||
// 1001: 44.1 kHz.
|
||||
return 44100
|
||||
case 0xA:
|
||||
// 1010: 48 kHz.
|
||||
return 48000
|
||||
case 0xB:
|
||||
// 1011: 96 kHz.
|
||||
return 96000
|
||||
case 0xC:
|
||||
// 1100: get 8 bit sample rate (in kHz) from the end of the header.
|
||||
return uint32(p.SampleRateExtra * 1000)
|
||||
case 0xD:
|
||||
// 1101: get 16 bit sample rate (in Hz) from the end of the header.
|
||||
return uint32(p.SampleRateExtra)
|
||||
case 0xE:
|
||||
// 1110: get 16 bit sample rate (in daHz) from the end of the header.
|
||||
return uint32(p.SampleRateExtra * 10)
|
||||
default:
|
||||
// 1111: invalid.
|
||||
return 0
|
||||
}
|
||||
return 0, io.ErrNoProgress
|
||||
}
|
||||
|
||||
func (o *FLACPacketizer) Close() error {
|
||||
return nil
|
||||
func (p *FLACPacket) getBitsPerSample() uint8 {
|
||||
switch p.BitsPerSample {
|
||||
case 0x0:
|
||||
// 000: unknown bits-per-sample; get from StreamInfo.
|
||||
return p.hdr.BitsPerSample
|
||||
case 0x1:
|
||||
// 001: 8 bits-per-sample.
|
||||
return 8
|
||||
case 0x2:
|
||||
// 010: 12 bits-per-sample.
|
||||
return 12
|
||||
case 0x4:
|
||||
// 100: 16 bits-per-sample.
|
||||
return 16
|
||||
case 0x5:
|
||||
// 101: 20 bits-per-sample.
|
||||
return 20
|
||||
case 0x6:
|
||||
// 110: 24 bits-per-sample.
|
||||
return 24
|
||||
default:
|
||||
// 011: reserved.
|
||||
// 111: reserved.
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func (o *FLACPacketizer) getBuffer() (buf []byte) {
|
||||
buf = make([]byte, len(o.buffer))
|
||||
copy(buf, o.buffer)
|
||||
o.buffer = o.buffer[:0]
|
||||
return
|
||||
func (p *FLACPacket) GetDataOffset(offset int64) []byte {
|
||||
//TODO: reserve proper amount
|
||||
buf := bytes.NewBuffer(make([]byte, 0, len(p.FrameData)+64))
|
||||
bw := bitio.NewWriter(buf)
|
||||
|
||||
//sync
|
||||
_ = bw.WriteBits(0b11111111111110, 14)
|
||||
|
||||
//reserved
|
||||
_ = bw.WriteBits(0, 1)
|
||||
|
||||
_ = bw.WriteBits(p.BlockingStrategy, 1)
|
||||
|
||||
_ = bw.WriteBits(p.BlockSizeRaw, 4)
|
||||
|
||||
_ = bw.WriteBits(p.SampleRateRaw, 4)
|
||||
|
||||
_ = bw.WriteBits(p.ChannelAssignment, 4)
|
||||
|
||||
_ = bw.WriteBits(p.BitsPerSample, 3)
|
||||
|
||||
//reserved
|
||||
_ = bw.WriteBits(0, 1)
|
||||
|
||||
number := int64(p.Number)
|
||||
if offset != 0 {
|
||||
if p.BlockingStrategy == 1 {
|
||||
number += offset
|
||||
|
||||
if number < 0 {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
number += offset / int64(p.getBlockSize())
|
||||
|
||||
if number < 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
if err = func(w io.ByteWriter, x uint64) error {
|
||||
const (
|
||||
tx = 0x80 // 1000 0000
|
||||
t2 = 0xC0 // 1100 0000
|
||||
t3 = 0xE0 // 1110 0000
|
||||
t4 = 0xF0 // 1111 0000
|
||||
t5 = 0xF8 // 1111 1000
|
||||
t6 = 0xFC // 1111 1100
|
||||
t7 = 0xFE // 1111 1110
|
||||
t8 = 0xFF // 1111 1111
|
||||
|
||||
maskx = 0x3F // 0011 1111
|
||||
mask2 = 0x1F // 0001 1111
|
||||
mask3 = 0x0F // 0000 1111
|
||||
mask4 = 0x07 // 0000 0111
|
||||
mask5 = 0x03 // 0000 0011
|
||||
mask6 = 0x01 // 0000 0001
|
||||
|
||||
rune1Max = 1<<7 - 1
|
||||
rune2Max = 1<<11 - 1
|
||||
rune3Max = 1<<16 - 1
|
||||
rune4Max = 1<<21 - 1
|
||||
rune5Max = 1<<26 - 1
|
||||
rune6Max = 1<<31 - 1
|
||||
rune7Max = 1<<36 - 1
|
||||
)
|
||||
|
||||
// 1-byte, 7-bit sequence?
|
||||
if x <= rune1Max {
|
||||
if err := w.WriteByte(byte(x)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// get number of continuation bytes and store bits of c0.
|
||||
var (
|
||||
// number of continuation bytes.,
|
||||
l int
|
||||
// bits of c0.
|
||||
bits uint64
|
||||
)
|
||||
switch {
|
||||
case x <= rune2Max:
|
||||
// if c0 == 110xxxxx
|
||||
// total: 11 bits (5 + 6)
|
||||
l = 1
|
||||
bits = t2 | (x>>6)&mask2
|
||||
case x <= rune3Max:
|
||||
// if c0 == 1110xxxx
|
||||
// total: 16 bits (4 + 6 + 6)
|
||||
l = 2
|
||||
bits = t3 | (x>>(6*2))&mask3
|
||||
case x <= rune4Max:
|
||||
// if c0 == 11110xxx
|
||||
// total: 21 bits (3 + 6 + 6 + 6)
|
||||
l = 3
|
||||
bits = t4 | (x>>(6*3))&mask4
|
||||
case x <= rune5Max:
|
||||
// if c0 == 111110xx
|
||||
// total: 26 bits (2 + 6 + 6 + 6 + 6)
|
||||
l = 4
|
||||
bits = t5 | (x>>(6*4))&mask5
|
||||
case x <= rune6Max:
|
||||
// if c0 == 1111110x
|
||||
// total: 31 bits (1 + 6 + 6 + 6 + 6 + 6)
|
||||
l = 5
|
||||
bits = t6 | (x>>(6*5))&mask6
|
||||
case x <= rune7Max:
|
||||
// if c0 == 11111110
|
||||
// total: 36 bits (0 + 6 + 6 + 6 + 6 + 6 + 6)
|
||||
l = 6
|
||||
bits = 0
|
||||
}
|
||||
// Store bits of c0.
|
||||
if err := w.WriteByte(byte(bits)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Store continuation bytes.
|
||||
for i := l - 1; i >= 0; i-- {
|
||||
bits = tx | (x>>uint(6*i))&maskx
|
||||
if err := w.WriteByte(byte(bits)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}(bw, uint64(number)); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if p.BlockSizeRaw == 0x6 {
|
||||
_ = bw.WriteBits(p.BlockSizeExtra, 8)
|
||||
} else if p.BlockSizeRaw == 0x7 {
|
||||
_ = bw.WriteBits(p.BlockSizeExtra, 16)
|
||||
}
|
||||
|
||||
if p.SampleRateRaw == 0xC {
|
||||
_ = bw.WriteBits(p.SampleRateExtra, 8)
|
||||
} else if p.SampleRateRaw == 0xD || p.SampleRateRaw == 0xE {
|
||||
_ = bw.WriteBits(p.SampleRateExtra, 16)
|
||||
}
|
||||
|
||||
_, _ = bw.Align()
|
||||
|
||||
_ = bw.WriteBits(uint64(CRC8ChecksumATM(buf.Bytes())), 8)
|
||||
|
||||
_, _ = bw.Write(p.FrameData)
|
||||
|
||||
_, _ = bw.Align()
|
||||
|
||||
_ = bw.WriteBits(uint64(CRC16ChecksumIBM(buf.Bytes())), 16)
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
type FLACPacketizer struct {
|
||||
br *bitio.Reader
|
||||
hdr *FLACHeaderPacket
|
||||
offset int64
|
||||
samples int64
|
||||
bytesRead int64
|
||||
buffer []byte
|
||||
}
|
||||
|
||||
func (o *FLACPacketizer) GetPacket() Packet {
|
||||
if o.hdr == nil {
|
||||
o.hdr = readFlacHeader(o.br)
|
||||
if o.hdr == nil {
|
||||
return nil
|
||||
}
|
||||
return o.hdr
|
||||
}
|
||||
|
||||
packet := readFlacFrame(o.hdr, o.br)
|
||||
if packet == nil {
|
||||
return nil
|
||||
}
|
||||
return packet
|
||||
}
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
//go:build !disable_format_flac && !disable_codec_libflac && cgo
|
||||
|
||||
package packetizer
|
||||
|
||||
import (
|
||||
libflac "git.gammaspectra.live/S.O.N.G/goflac"
|
||||
"io"
|
||||
)
|
||||
|
||||
type FLACPacketizer struct {
|
||||
reader io.Reader
|
||||
stream *libflac.Decoder
|
||||
offset int64
|
||||
samples int64
|
||||
bytesRead int64
|
||||
buffer []byte
|
||||
}
|
||||
|
||||
func (o *FLACPacketizer) GetPacket() Packet {
|
||||
var err error
|
||||
if o.stream == nil {
|
||||
o.stream, err = libflac.NewDecoderReader(o)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return &FLACPacket{
|
||||
mode: Keep,
|
||||
sampleNumber: 0,
|
||||
endSampleNumber: 0,
|
||||
data: o.getBuffer(),
|
||||
}
|
||||
}
|
||||
|
||||
frame, err := o.stream.ReadFrame()
|
||||
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
sampleNumber := o.samples
|
||||
nsamples := int64(len(frame.Buffer)) / int64(frame.Channels)
|
||||
o.samples += nsamples
|
||||
|
||||
return &FLACPacket{
|
||||
mode: Discard,
|
||||
sampleNumber: sampleNumber,
|
||||
endSampleNumber: sampleNumber + nsamples,
|
||||
data: o.getBuffer(),
|
||||
}
|
||||
}
|
200
audio/packetizer/flac_crc.go
Normal file
200
audio/packetizer/flac_crc.go
Normal file
|
@ -0,0 +1,200 @@
|
|||
package packetizer
|
||||
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This implements the 8-bit cyclic redundancy check, or CRC-8,
|
||||
// checksum. See http://en.wikipedia.org/wiki/Cyclic_redundancy_check and
|
||||
// http://www.ross.net/crc/download/crc_v3.txt for information.
|
||||
|
||||
// CRC8Size of a CRC-8 checksum in bytes.
|
||||
const CRC8Size = 1
|
||||
|
||||
// Predefined polynomials.
|
||||
const (
|
||||
CRC8ATM = 0x07 // x^8 + x^2 + x + 1
|
||||
)
|
||||
|
||||
// CRC8Table is a 256-word table representing the polynomial for efficient
|
||||
// processing.
|
||||
type CRC8Table [256]uint8
|
||||
|
||||
// CRC8ATMTable is the table for the CRC8ATM polynomial.
|
||||
var CRC8ATMTable = makeTableCRC8(CRC8ATM)
|
||||
|
||||
// makeTableCRC8 returns the CRC8Table constructed from the specified polynomial.
|
||||
func makeTableCRC8(poly uint8) (table *CRC8Table) {
|
||||
table = new(CRC8Table)
|
||||
for i := range table {
|
||||
crc := uint8(i)
|
||||
for j := 0; j < 8; j++ {
|
||||
if crc&0x80 != 0 {
|
||||
crc = crc<<1 ^ poly
|
||||
} else {
|
||||
crc <<= 1
|
||||
}
|
||||
}
|
||||
table[i] = crc
|
||||
}
|
||||
return table
|
||||
}
|
||||
|
||||
// crc16Digest represents the partial evaluation of a checksum.
|
||||
type crc8Digest struct {
|
||||
crc uint8
|
||||
table *CRC8Table
|
||||
}
|
||||
|
||||
// NewCRC8 creates a new hash computing the CRC-8 checksum using the
|
||||
// polynomial represented by the CRC8Table.
|
||||
func NewCRC8(table *CRC8Table) *crc8Digest {
|
||||
return &crc8Digest{0, table}
|
||||
}
|
||||
|
||||
// NewCRC8ATM creates a new hashutil.Hash8 computing the CRC-8 checksum using the
|
||||
// CRC8ATM polynomial.
|
||||
func NewCRC8ATM() *crc8Digest {
|
||||
return NewCRC8(CRC8ATMTable)
|
||||
}
|
||||
|
||||
func (d *crc8Digest) Size() int {
|
||||
return CRC8Size
|
||||
}
|
||||
|
||||
func (d *crc8Digest) BlockSize() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
func (d *crc8Digest) Reset() {
|
||||
d.crc = 0
|
||||
}
|
||||
|
||||
// CRC8Update returns the result of adding the bytes in p to the crc.
|
||||
func CRC8Update(crc uint8, table *CRC8Table, p []byte) uint8 {
|
||||
for _, v := range p {
|
||||
crc = table[crc^v]
|
||||
}
|
||||
return crc
|
||||
}
|
||||
|
||||
func (d *crc8Digest) Write(p []byte) (n int, err error) {
|
||||
d.crc = CRC8Update(d.crc, d.table, p)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// Sum8 returns the 8-bit checksum of the hash.
|
||||
func (d *crc8Digest) Sum8() uint8 {
|
||||
return d.crc
|
||||
}
|
||||
|
||||
func (d *crc8Digest) Sum(in []byte) []byte {
|
||||
return append(in, d.crc)
|
||||
}
|
||||
|
||||
// CRC8Checksum returns the CRC-8 checksum of data, using the polynomial represented
|
||||
// by the CRC8Table.
|
||||
func CRC8Checksum(data []byte, table *CRC8Table) uint8 {
|
||||
return CRC8Update(0, table, data)
|
||||
}
|
||||
|
||||
// CRC8ChecksumATM returns the CRC-8 checksum of data using the CRC8ATM polynomial.
|
||||
func CRC8ChecksumATM(data []byte) uint8 {
|
||||
return CRC8Update(0, CRC8ATMTable, data)
|
||||
}
|
||||
|
||||
// CRC16Size of a CRC-16 checksum in bytes.
|
||||
const CRC16Size = 2
|
||||
|
||||
// Predefined polynomials.
|
||||
const (
|
||||
CRC16IBM = 0x8005 // x^16 + x^15 + x^2 + x^0
|
||||
)
|
||||
|
||||
// CRC16Table is a 256-word table representing the polynomial for efficient
|
||||
// processing.
|
||||
type CRC16Table [256]uint16
|
||||
|
||||
// CRC16IBMTable is the table for the CRC16IBM polynomial.
|
||||
var CRC16IBMTable = makeTablecrc16(CRC16IBM)
|
||||
|
||||
// makeTablecrc16 returns the CRC16Table constructed from the specified polynomial.
|
||||
func makeTablecrc16(poly uint16) (table *CRC16Table) {
|
||||
table = new(CRC16Table)
|
||||
for i := range table {
|
||||
crc := uint16(i << 8)
|
||||
for j := 0; j < 8; j++ {
|
||||
if crc&0x8000 != 0 {
|
||||
crc = crc<<1 ^ poly
|
||||
} else {
|
||||
crc <<= 1
|
||||
}
|
||||
}
|
||||
table[i] = crc
|
||||
}
|
||||
return table
|
||||
}
|
||||
|
||||
// crc16Digest represents the partial evaluation of a checksum.
|
||||
type crc16Digest struct {
|
||||
crc uint16
|
||||
table *CRC16Table
|
||||
}
|
||||
|
||||
// NewCRC16 creates a new hash computing the CRC-16 checksum using the
|
||||
// polynomial represented by the CRC16Table.
|
||||
func NewCRC16(table *CRC16Table) *crc16Digest {
|
||||
return &crc16Digest{0, table}
|
||||
}
|
||||
|
||||
// NewCRC16IBM creates a new hash computing the CRC-16 checksum using the
|
||||
// CRC16IBM polynomial.
|
||||
func NewCRC16IBM() *crc16Digest {
|
||||
return NewCRC16(CRC16IBMTable)
|
||||
}
|
||||
|
||||
func (d *crc16Digest) Size() int {
|
||||
return CRC16Size
|
||||
}
|
||||
|
||||
func (d *crc16Digest) BlockSize() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
func (d *crc16Digest) Reset() {
|
||||
d.crc = 0
|
||||
}
|
||||
|
||||
// CRC16Update returns the result of adding the bytes in p to the crc.
|
||||
func CRC16Update(crc uint16, table *CRC16Table, p []byte) uint16 {
|
||||
for _, v := range p {
|
||||
crc = crc<<8 ^ table[crc>>8^uint16(v)]
|
||||
}
|
||||
return crc
|
||||
}
|
||||
|
||||
func (d *crc16Digest) Write(p []byte) (n int, err error) {
|
||||
d.crc = CRC16Update(d.crc, d.table, p)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// Sum16 returns the 16-bit checksum of the hash.
|
||||
func (d *crc16Digest) Sum16() uint16 {
|
||||
return d.crc
|
||||
}
|
||||
|
||||
func (d *crc16Digest) Sum(in []byte) []byte {
|
||||
s := d.Sum16()
|
||||
return append(in, byte(s>>8), byte(s))
|
||||
}
|
||||
|
||||
// CRC16Checksum returns the CRC-16 checksum of data, using the polynomial
|
||||
// represented by the CRC16Table.
|
||||
func CRC16Checksum(data []byte, table *CRC16Table) uint16 {
|
||||
return CRC16Update(0, table, data)
|
||||
}
|
||||
|
||||
// CRC16ChecksumIBM returns the CRC-16 checksum of data using the CRC16IBM polynomial.
|
||||
func CRC16ChecksumIBM(data []byte) uint16 {
|
||||
return CRC16Update(0, CRC16IBMTable, data)
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
//go:build disable_format_flac || disable_codec_libflac || !cgo
|
||||
|
||||
package packetizer
|
||||
|
||||
import (
|
||||
flac_parser "git.gammaspectra.live/S.O.N.G/flacgo"
|
||||
"io"
|
||||
)
|
||||
|
||||
type FLACPacketizer struct {
|
||||
reader io.Reader
|
||||
stream *flac_parser.Stream
|
||||
offset int64
|
||||
samples int64
|
||||
bytesRead int64
|
||||
buffer []byte
|
||||
}
|
||||
|
||||
func (o *FLACPacketizer) GetPacket() Packet {
|
||||
var err error
|
||||
if o.stream == nil {
|
||||
o.stream, err = flac_parser.NewSeek(o)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return &FLACPacket{
|
||||
mode: Keep,
|
||||
sampleNumber: 0,
|
||||
endSampleNumber: 0,
|
||||
data: o.getBuffer(),
|
||||
}
|
||||
}
|
||||
|
||||
frame, err := o.stream.ParseNext()
|
||||
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
sampleNumber := int64(frame.Num)
|
||||
if frame.HasFixedBlockSize {
|
||||
sampleNumber *= int64(frame.BlockSize)
|
||||
}
|
||||
return &FLACPacket{
|
||||
mode: Discard,
|
||||
sampleNumber: sampleNumber,
|
||||
endSampleNumber: sampleNumber + int64(frame.Subframes[0].NSamples),
|
||||
data: o.getBuffer(),
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
//go:build !disable_format_flac && !disable_codec_libflac && cgo
|
||||
//go:build !disable_format_flac
|
||||
|
||||
package packetizer
|
||||
|
||||
|
@ -63,3 +63,49 @@ func TestPacketizeFLAC(t *testing.T) {
|
|||
t.Errorf("Mismatch on byte output %X != %X", shaPacketHasher.Sum([]byte{}), shaHasher.Sum([]byte{}))
|
||||
}
|
||||
}
|
||||
|
||||
func TestPacketizeFLACOffset(t *testing.T) {
|
||||
t.Parallel()
|
||||
fp, err := os.Open(test.TestSingleSample24)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer fp.Close()
|
||||
source, err := flac.NewFormat().Open(fp)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
reader, writer := io.Pipe()
|
||||
|
||||
go func() {
|
||||
defer writer.Close()
|
||||
err = flac.NewFormat().Encode(source, writer, nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
pipePacketizer := NewFLACPacketizer(reader)
|
||||
packetCount := 0
|
||||
packetBytes := 0
|
||||
for {
|
||||
packet := pipePacketizer.GetPacket()
|
||||
if packet == nil {
|
||||
break
|
||||
}
|
||||
packetCount++
|
||||
if offsetablePacket, ok := packet.(OffsetablePacket); ok {
|
||||
packetBytes += len(offsetablePacket.GetDataOffset(4096))
|
||||
}
|
||||
}
|
||||
if packetCount != 4231 {
|
||||
t.Errorf("Wrong Packet Count %d != %d", packetCount, 4231)
|
||||
}
|
||||
if packetBytes < 50000000 {
|
||||
t.Errorf("Wrong Packet Bytes %d < %d", packetBytes, 50000000)
|
||||
}
|
||||
}
|
|
@ -93,7 +93,7 @@ func TestPacketizeOggFLACOffset(t *testing.T) {
|
|||
}
|
||||
packetCount++
|
||||
if offsetablePacket, ok := packet.(OffsetablePacket); ok {
|
||||
packetBytes += len(offsetablePacket.GetDataOffset(1000))
|
||||
packetBytes += len(offsetablePacket.GetDataOffset(4096))
|
||||
}
|
||||
}
|
||||
if packetCount != 1059 {
|
||||
|
|
2
go.mod
2
go.mod
|
@ -14,6 +14,7 @@ require (
|
|||
github.com/dh1tw/gosamplerate v0.1.2
|
||||
github.com/edgeware/mp4ff v0.29.0
|
||||
github.com/hajimehoshi/go-mp3 v0.3.3
|
||||
github.com/icza/bitio v1.1.0
|
||||
github.com/jfreymuth/oggvorbis v1.0.3
|
||||
github.com/kvark128/minimp3 v0.0.0-20220408223524-dd428325fce7
|
||||
github.com/minio/sha256-simd v1.0.0
|
||||
|
@ -23,7 +24,6 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
github.com/icza/bitio v1.1.0 // indirect
|
||||
github.com/jfreymuth/vorbis v1.0.2 // indirect
|
||||
github.com/klauspost/cpuid v1.3.1 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.0.4 // indirect
|
||||
|
|
Loading…
Reference in a new issue