ADTS/MP3/Ogg packetizers

This commit is contained in:
DataHoarder 2022-03-01 18:10:01 +01:00
parent 4a7b6f3bea
commit 73e94c24c4
7 changed files with 212 additions and 2 deletions

View file

@ -1,14 +1,16 @@
# [![](resources/kirikas.png)](resources/kirika.png) Kirika
Collection of audio utilities for decoding/encoding files and streams.
* Not based on ffmpeg/libav/libavcodec/libavformat and alike
* Channel-based audio consumption/filter chain
* Raw sample analyzer channels
* AnalyzerChannel channels / mergers / splitters / trimmers
* Audio resampler
* Audio downmixing to stereo/mono
* Multi-format decoder and encoder
* Multi-codec decoder and encoder
* Multi-format packetizers
## Formats and codecs supported
## Codecs supported
| Codec | Container | Decoder | Analyzer | Encoder | Notes |
|:--------:|:---------:|:-------:|:--------:|:-------:|:------------------------------------------------------------------------------------------------------------------------------------------------------|
@ -18,6 +20,16 @@ Collection of audio utilities for decoding/encoding files and streams.
| **Opus** | Ogg | ✅ | - | ✅ | Adjustable encoding bitrate. |
| **AAC** | ADTS | ❌ | - | ✅ | Adjustable encoding bitrate and mode (LC, HEv2). |
## Container packetizers supported
| Container | Packetizer | Keep Mode | Sample Number |
|:---------:|:----------:|:---------:|:-------------:|
| **FLAC** | ❌ | - | - |
| **TTA** | ❌ | - | - |
| **MP3** | ✅ | ✅ | ✅ |
| **Ogg** | ✅ | ✅ | ✅* |
| **ADTS** | ✅ | ✅ | ❌ |
## Dependencies
### Go >= 1.18

50
audio/packetizer/adts.go Normal file
View file

@ -0,0 +1,50 @@
package packetizer
import (
aac_adts "github.com/edgeware/mp4ff/aac"
"io"
)
type AdtsPacketizer struct {
reader io.Reader
samples int64
}
type AdtsPacket struct {
header *aac_adts.ADTSHeader
data []byte
}
func (p *AdtsPacket) KeepMode() KeepMode {
return Discard
}
func (p *AdtsPacket) GetSampleNumber() int64 {
return 0
}
func (p *AdtsPacket) GetData() []byte {
return append(p.header.Encode(), p.data...)
}
func NewAdtsPacketizer(reader io.Reader) *AdtsPacketizer {
return &AdtsPacketizer{reader: reader}
}
func (o *AdtsPacketizer) GetPacket() Packet {
packet := &AdtsPacket{}
header, _, err := aac_adts.DecodeADTSHeader(o.reader)
if err != nil {
return nil
}
packet.header = header
packet.data = make([]byte, packet.header.PayloadLength)
if _, err := o.reader.Read(packet.data); err != nil {
return nil
}
return packet
}

47
audio/packetizer/mp3.go Normal file
View file

@ -0,0 +1,47 @@
package packetizer
import (
mp3parser "github.com/sssgun/mp3"
"io"
)
type Mp3Packetizer struct {
decoder *mp3parser.Decoder
samples int64
}
type Mp3Packet struct {
sampleNumber int64
frame mp3parser.Frame
}
func (p *Mp3Packet) KeepMode() KeepMode {
return Discard
}
func (p *Mp3Packet) GetSampleNumber() int64 {
return p.sampleNumber
}
func (p *Mp3Packet) GetData() []byte {
return p.frame.Bytes()
}
func NewMp3Packetizer(reader io.Reader) *Mp3Packetizer {
return &Mp3Packetizer{decoder: mp3parser.NewDecoder(reader)}
}
func (o *Mp3Packetizer) GetPacket() Packet {
packet := &Mp3Packet{}
var skipped int
if err := o.decoder.Decode(&packet.frame, &skipped); err != nil {
return nil
}
packet.sampleNumber = o.samples
o.samples += int64(packet.frame.Samples())
return packet
}

73
audio/packetizer/ogg.go Normal file
View file

@ -0,0 +1,73 @@
package packetizer
import (
"bytes"
"encoding/binary"
"io"
)
type OggPacketizer struct {
reader io.Reader
}
type OggPacket struct {
Capture [4]byte
Revision byte
Flags byte
AbsoluteGranulePosition int64
StreamSerialNumber uint32
PageChecksum uint32
PageSegments byte
pageSegmentTable []byte
segments [][]byte
}
func (p *OggPacket) KeepMode() KeepMode {
return Discard
}
func (p *OggPacket) GetSampleNumber() int64 {
//TODO
return p.AbsoluteGranulePosition
}
func (p *OggPacket) GetData() []byte {
buf := bytes.NewBuffer(make([]byte, 0, 4+1+1+8+4+4+1+p.PageChecksum*(255+1)))
binary.Write(buf, binary.BigEndian, p)
binary.Write(buf, binary.BigEndian, p.pageSegmentTable)
for _, segment := range p.segments {
if len(segment) > 0 {
binary.Write(buf, binary.BigEndian, segment)
}
}
return buf.Bytes()
}
func NewOggPacketizer(reader io.Reader) *OggPacketizer {
return &OggPacketizer{reader: reader}
}
func (o *OggPacketizer) GetPacket() Packet {
packet := &OggPacket{}
if err := binary.Read(o.reader, binary.BigEndian, packet); err != nil || bytes.Compare(packet.Capture[:], []byte{'O', 'g', 'g', 'S'}) != 0 {
return nil
}
packet.pageSegmentTable = make([]byte, packet.PageSegments)
if err := binary.Read(o.reader, binary.BigEndian, packet.pageSegmentTable); err != nil {
return nil
}
packet.segments = make([][]byte, packet.PageSegments)
for i, size := range packet.pageSegmentTable {
if size > 0 {
packet.segments[i] = make([]byte, size)
if err := binary.Read(o.reader, binary.BigEndian, packet.segments[i]); err != nil {
return nil
}
}
}
return packet
}

View file

@ -0,0 +1,20 @@
package packetizer
type KeepMode int
const (
KeepLast = KeepMode(iota)
GroupKeep
GroupDiscard
Discard
)
type Packet interface {
KeepMode() KeepMode
GetSampleNumber() int64
GetData() []byte
}
type Packetizer interface {
GetPacket() Packet
}

2
go.mod
View file

@ -8,7 +8,9 @@ require (
git.gammaspectra.live/S.O.N.G/go-tta v0.2.1-0.20220226150007-096de1072bd6
git.gammaspectra.live/S.O.N.G/goflac v0.0.0-20220223152921-827e6c3f729f
github.com/dh1tw/gosamplerate v0.1.2
github.com/edgeware/mp4ff v0.26.1
github.com/kvark128/minimp3 v0.0.0-20211109174940-101188771a65
github.com/sssgun/mp3 v0.0.0-20170810093403-85f2ec632081
github.com/viert/go-lame v0.0.0-20201108052322-bb552596b11d
)

6
go.sum
View file

@ -9,9 +9,15 @@ git.gammaspectra.live/S.O.N.G/goflac v0.0.0-20220223152921-827e6c3f729f/go.mod h
github.com/cocoonlife/testify v0.0.0-20160218172820-792cc1faeb64 h1:LjPYdzoFSAJ5Tr/ElL8kzTJghXgpnOjJVbgd1UvZB1o=
github.com/dh1tw/gosamplerate v0.1.2 h1:oyqtZk67xB9B4l+vIZCZ3F0RYV/z66W58VOah11/ktI=
github.com/dh1tw/gosamplerate v0.1.2/go.mod h1:zooTyHpoR7hE+FLfdE3yjLHb2QA2NpMusNfuaZqEACM=
github.com/edgeware/mp4ff v0.26.1 h1:tH+TIesZZmrA8BN5HuiKWp3sv5NF4N1A2cFxTSCNL8E=
github.com/edgeware/mp4ff v0.26.1/go.mod h1:6VHE5CTkpDseIg775+rh8BfnTvqjMnVbz5EDU4QwSdc=
github.com/go-test/deep v1.0.6 h1:UHSEyLZUwX9Qoi99vVwvewiMC8mM2bf7XEM2nqvzEn8=
github.com/go-test/deep v1.0.6/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8=
github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s=
github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4=
github.com/kvark128/minimp3 v0.0.0-20211109174940-101188771a65 h1:8qfVQv7MSACDXadEwl1yjUKJ68yC9B7nR4cioEoCfH0=
github.com/kvark128/minimp3 v0.0.0-20211109174940-101188771a65/go.mod h1:hIq9nAqNcwTySvnFhCe1C8xC/STIr2Fe5vJ52zk1jkE=
github.com/sssgun/mp3 v0.0.0-20170810093403-85f2ec632081 h1:Qo/HswJzVywl0podyXMD62HIohsj/Ij2oXbD26aUIxM=
github.com/sssgun/mp3 v0.0.0-20170810093403-85f2ec632081/go.mod h1:ExwZtltybPz8zLO8c2lKRfpPk1HAxhrkp038QIBs+yg=
github.com/viert/go-lame v0.0.0-20201108052322-bb552596b11d h1:LptdD7GTUZeklomtW5vZ1AHwBvDBUCZ2Ftpaz7uEI7g=
github.com/viert/go-lame v0.0.0-20201108052322-bb552596b11d/go.mod h1:EqTcYM7y4JlSfeTI47pmNu3EZQuCuLQefsQyg1Imlz8=