MeteorLight/listener/aps1/aps1.go
DataHoarder 68e7cfca07
All checks were successful
continuous-integration/drone/push Build is passing
Use exp/slice Remove, bump Kirika, cleanup go inspection
2022-10-03 11:56:54 +02:00

202 lines
5.1 KiB
Go

package aps1
import (
"bytes"
"encoding/binary"
"encoding/json"
"errors"
"git.gammaspectra.live/S.O.N.G/Kirika/audio/packetizer"
"git.gammaspectra.live/S.O.N.G/MeteorLight/listener"
"git.gammaspectra.live/S.O.N.G/MeteorLight/queue/metadata"
"sync/atomic"
)
type Listener struct {
information listener.Information
started atomic.Bool
streamStartOffset int64
streamSamplesBuffer int64
writer listener.WriterFunc
waiter chan struct{}
offsetStart bool
headerBytes []byte
}
func NewListener(info listener.Information, writer listener.WriterFunc, samplesToBuffer int64, offsetStart bool, channels, sampleRate int, mimeType string) (*Listener, map[string]string) {
headers := make(map[string]string)
headers["x-audio-packet-stream"] = "1"
headers["content-type"] = "application/x-audio-packet-stream"
headerBytes := new(bytes.Buffer)
_ = binary.Write(headerBytes, binary.LittleEndian, int64(channels))
_ = binary.Write(headerBytes, binary.LittleEndian, int64(sampleRate))
_ = binary.Write(headerBytes, binary.LittleEndian, int32(len(mimeType)))
headerBytes.Write([]byte(mimeType))
return &Listener{
information: info,
writer: writer,
offsetStart: offsetStart,
streamSamplesBuffer: samplesToBuffer,
streamStartOffset: -1,
headerBytes: headerBytes.Bytes(),
waiter: make(chan struct{}),
}, headers
}
func (l *Listener) Wait() {
<-l.waiter
}
func (l *Listener) Identifier() string {
return l.information.Identifier
}
func (l *Listener) Information() *listener.Information {
return &l.information
}
func (l *Listener) HasStarted() bool {
return l.started.Load()
}
func (l *Listener) Start(packets []packetizer.Packet) error {
if l.started.Swap(true) {
return nil
}
if err := l.writer((&packetStreamFrame{
Type: Header,
Category: 0,
StartSampleNumber: 0,
DurationInSamples: 0,
Data: l.headerBytes,
}).Encode()); err != nil {
return err
}
if len(packets) > 0 {
sampleBufferMin := packets[len(packets)-1].GetStartSampleNumber() - l.streamSamplesBuffer
for _, p := range packets {
if p.KeepMode() != packetizer.Discard || p.GetEndSampleNumber() >= sampleBufferMin {
if err := l.Write(p); err != nil {
return err
}
}
}
}
return nil
}
func (l *Listener) Write(packet packetizer.Packet) error {
if metadataPacket, ok := packet.(*metadata.Packet); ok {
queueInfoBuf := make([]byte, binary.MaxVarintLen64)
n := binary.PutVarint(queueInfoBuf, int64(metadataPacket.TrackEntry.Identifier))
if err := l.writer((&packetStreamFrame{
Type: TrackIdentifier,
Category: packet.Category(),
StartSampleNumber: packet.GetStartSampleNumber(),
DurationInSamples: packet.GetEndSampleNumber() - packet.GetStartSampleNumber(),
Data: queueInfoBuf[:n],
}).Encode()); err != nil {
return err
}
if metadataBytes, err := json.Marshal(metadataPacket.TrackEntry.Metadata); err == nil {
if err := l.writer((&packetStreamFrame{
Type: TrackMetadata,
Category: packet.Category(),
StartSampleNumber: packet.GetStartSampleNumber(),
DurationInSamples: packet.GetEndSampleNumber() - packet.GetStartSampleNumber(),
Data: metadataBytes,
}).Encode()); err != nil {
return err
}
}
return nil
}
var frameType PacketStreamType
switch packet.KeepMode() {
case packetizer.KeepLast:
frameType = DataKeepLast
case packetizer.Keep:
frameType = DataKeep
case packetizer.GroupKeep:
frameType = DataGroupKeep
case packetizer.GroupDiscard:
frameType = DataGroupDiscard
case packetizer.Discard:
frameType = DataDiscard
default:
return errors.New("unknown KeepMode")
}
return l.writer((&packetStreamFrame{
Type: frameType,
Category: packet.Category(),
StartSampleNumber: packet.GetStartSampleNumber(),
DurationInSamples: packet.GetEndSampleNumber() - packet.GetStartSampleNumber(),
Data: packet.GetData(),
}).Encode())
}
func (l *Listener) Close() {
_ = l.writer(nil)
close(l.waiter)
}
type PacketStreamType uint64
// PacketStreamType The order of these fields is important and set on-wire protocol
const (
Header = PacketStreamType(iota)
DataKeepLast
DataKeep
DataGroupKeep
DataGroupDiscard
DataDiscard
TrackIdentifier
TrackMetadata
)
type packetStreamFrame struct {
Type PacketStreamType
Category int64
StartSampleNumber int64
DurationInSamples int64
//automatically filled based on Data
//Size uint64
Data []byte
}
func (p *packetStreamFrame) Encode() []byte {
buf := new(bytes.Buffer)
varBuf := make([]byte, binary.MaxVarintLen64)
n := binary.PutUvarint(varBuf, uint64(p.Type))
buf.Write(varBuf[:n])
n = binary.PutUvarint(varBuf, uint64(p.Category))
buf.Write(varBuf[:n])
n = binary.PutVarint(varBuf, p.StartSampleNumber)
buf.Write(varBuf[:n])
n = binary.PutVarint(varBuf, p.DurationInSamples)
buf.Write(varBuf[:n])
n = binary.PutUvarint(varBuf, uint64(len(p.Data)))
buf.Write(varBuf[:n])
buf.Write(p.Data)
return buf.Bytes()
}