117 lines
2.9 KiB
Go
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
|
|
}
|