ADTS/MP3/Ogg packetizers
This commit is contained in:
parent
4a7b6f3bea
commit
73e94c24c4
16
README.md
16
README.md
|
@ -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
50
audio/packetizer/adts.go
Normal 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
47
audio/packetizer/mp3.go
Normal 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
73
audio/packetizer/ogg.go
Normal 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
|
||||
}
|
20
audio/packetizer/packetizer.go
Normal file
20
audio/packetizer/packetizer.go
Normal 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
2
go.mod
|
@ -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
6
go.sum
|
@ -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=
|
||||
|
|
Loading…
Reference in a new issue