Kirika/audio/packetizer/ogg.go
DataHoarder b7ce508d50
Some checks failed
continuous-integration/drone/push Build is failing
Expose internal structure of packets on packetizer
2022-11-07 14:02:07 +01:00

117 lines
2.9 KiB
Go

package packetizer
import (
"bufio"
"bytes"
"encoding/binary"
"io"
"unsafe"
)
type OggPacketizer struct {
reader io.Reader
samples int64
}
type oggPacketHeader struct {
Capture [4]byte
Revision byte
Flags byte
AbsoluteGranulePosition int64
StreamSerialNumber uint32
PageSequenceNumber uint32
PageChecksum uint32
PageSegments byte
}
type OggPacket struct {
SampleNumber int64
Header oggPacketHeader
PageSegmentTable []byte
Segments [][]byte
}
func (p *OggPacket) KeepMode() KeepMode {
if p.Header.AbsoluteGranulePosition == 0 {
return Keep
}
return Discard
}
func (p *OggPacket) GetStartSampleNumber() int64 {
return p.SampleNumber
}
func (p *OggPacket) GetEndSampleNumber() int64 {
return p.Header.AbsoluteGranulePosition
}
func (p *OggPacket) Category() int64 {
return int64(p.Header.StreamSerialNumber)
}
func (p *OggPacket) GetData() []byte {
buf := bytes.NewBuffer(make([]byte, 0, 4+1+1+8+4+4+4+1+int(p.Header.PageSegments)*(255+1)))
_ = binary.Write(buf, binary.LittleEndian, p.Header)
_ = binary.Write(buf, binary.LittleEndian, p.PageSegmentTable)
for _, segment := range p.Segments {
if len(segment) > 0 {
_ = binary.Write(buf, binary.LittleEndian, segment)
}
}
return buf.Bytes()
}
func (p *OggPacket) GetDataOffset(offset int64) []byte {
buf := bytes.NewBuffer(make([]byte, 0, 4+1+1+8+4+4+4+1+int(p.Header.PageSegments)*(255+1)))
//rewrite header
headerClone := p.Header
headerClone.AbsoluteGranulePosition -= offset
headerClone.PageChecksum = 0
_ = binary.Write(buf, binary.LittleEndian, headerClone)
_ = binary.Write(buf, binary.LittleEndian, p.PageSegmentTable)
for _, segment := range p.Segments {
if len(segment) > 0 {
_ = binary.Write(buf, binary.LittleEndian, segment)
}
}
//recalculate CRC in-place
byteResult := buf.Bytes()
*(*uint32)(unsafe.Pointer(&byteResult[4+1+1+8+4+4])) = oggCrc32(byteResult)
return byteResult
}
func NewOggPacketizer(reader io.Reader) *OggPacketizer {
return &OggPacketizer{reader: bufio.NewReader(reader)}
}
func (o *OggPacketizer) GetPacket() Packet {
packet := &OggPacket{}
if err := binary.Read(o.reader, binary.LittleEndian, &packet.Header); err != nil || bytes.Compare(packet.Header.Capture[:], []byte{'O', 'g', 'g', 'S'}) != 0 {
return nil
}
packet.PageSegmentTable = make([]byte, packet.Header.PageSegments)
if err := binary.Read(o.reader, binary.LittleEndian, packet.PageSegmentTable); err != nil {
return nil
}
packet.Segments = make([][]byte, packet.Header.PageSegments)
for i, size := range packet.PageSegmentTable {
if size > 0 {
packet.Segments[i] = make([]byte, size)
if err := binary.Read(o.reader, binary.LittleEndian, packet.Segments[i]); err != nil {
return nil
}
}
}
//use previous sample number instead
packet.SampleNumber = o.samples
o.samples = packet.Header.AbsoluteGranulePosition
return packet
}