Kirika/audio/format/aac/libfdk-aac.go
2022-04-23 20:41:14 +02:00

485 lines
9.3 KiB
Go

//go:build !disable_format_aac && !disable_codec_libfdk_aac
// +build !disable_format_aac,!disable_codec_libfdk_aac
package aac
import (
"bytes"
"errors"
"fmt"
"git.gammaspectra.live/S.O.N.G/Kirika/audio"
"git.gammaspectra.live/S.O.N.G/Kirika/cgo"
"git.gammaspectra.live/S.O.N.G/go-fdkaac/fdkaac"
aac_adts "github.com/edgeware/mp4ff/aac"
"github.com/edgeware/mp4ff/mp4"
"io"
"time"
"unsafe"
)
type Format struct {
}
func NewFormat() Format {
return Format{}
}
func (f Format) Name() string {
return "aac"
}
func (f Format) Description() string {
return "libfdk-aac (S.O.N.G/go-fdkaac)"
}
func decodeFrame(decoder *fdkaac.AacDecoder, r io.Reader) ([]float32, error) {
pcm, err := tryDecodeFrame(decoder)
if err != nil {
return nil, err
}
if pcm != nil {
return pcm, err
}
header, _, err := aac_adts.DecodeADTSHeader(r)
if err != nil {
return nil, err
}
data := make([]byte, header.PayloadLength)
if _, err = io.ReadFull(r, data); err != nil {
return nil, err
}
var n int
fullData := append(header.Encode(), data...)
if n, err = decoder.Fill(fullData); n != 0 {
return nil, errors.New("buffer under read")
}
if err != nil {
return nil, err
}
return decodeFrame(decoder, r)
}
func tryDecodeFrame(decoder *fdkaac.AacDecoder) ([]float32, error) {
pcm, err := decoder.Decode()
if err != nil {
return nil, err
}
if pcm != nil {
return cgo.Int32ToFloat32(cgo.BytesToInt32(pcm, 16), 16), nil
}
return nil, nil
}
func decodeFrameMP4(decoder *fdkaac.AacDecoder, demuxer *mp4Decoder) (result []float32, err error) {
pcm, err := tryDecodeFrame(decoder)
if err != nil {
return nil, err
}
if pcm != nil {
return pcm, err
}
samples := demuxer.Read()
if samples == nil {
return nil, io.EOF
}
var n int
for _, sample := range samples {
if n, err = decoder.Fill(sample); n != 0 {
return nil, errors.New("buffer under read")
}
if err != nil {
return nil, err
}
pcm, err = tryDecodeFrame(decoder)
if pcm != nil {
result = append(result, pcm...)
}
}
return result, nil
}
func (f Format) Open(r io.ReadSeekCloser) (audio.Source, error) {
decoder := fdkaac.NewAacDecoder()
mp4Demuxer, err := tryDecodeMP4(r)
if err != nil { //try ADTS
r.Seek(0, io.SeekStart)
err = decoder.InitAdts()
if err != nil {
return audio.Source{}, err
}
buf, err := decodeFrame(decoder, r)
if err != nil {
decoder.Close()
return audio.Source{}, err
}
newChannel := make(chan []float32)
go func() {
defer close(newChannel)
defer decoder.Close()
if len(buf) > 0 {
newChannel <- buf
}
for {
buf, err = decodeFrame(decoder, r)
if err != nil {
return
}
if len(buf) > 0 {
newChannel <- buf
}
}
}()
return audio.Source{
Channels: decoder.NumChannels(),
SampleRate: decoder.SampleRate(),
Blocks: newChannel,
}, nil
} else {
return audio.Source{}, fmt.Errorf("unsupported format mp4")
err = decoder.InitRaw(mp4Demuxer.cookie)
if err != nil {
return audio.Source{}, err
}
buf, err := decodeFrameMP4(decoder, mp4Demuxer)
if err != nil {
decoder.Close()
return audio.Source{}, err
}
newChannel := make(chan []float32)
go func() {
defer close(newChannel)
defer decoder.Close()
if len(buf) > 0 {
newChannel <- buf
}
for {
buf, err = decodeFrameMP4(decoder, mp4Demuxer)
if err != nil {
return
}
if len(buf) > 0 {
newChannel <- buf
}
}
}()
return audio.Source{
Channels: decoder.NumChannels(),
SampleRate: decoder.SampleRate(),
Blocks: newChannel,
}, nil
}
}
func (f Format) Encode(source audio.Source, writer io.WriteCloser, options map[string]interface{}) error {
var bitrate = 128
var isHEv2 bool
var format = "adts"
if options != nil {
var val interface{}
var ok bool
var intVal int
var strVal string
if val, ok = options["bitrate"]; ok {
if strVal, ok = val.(string); ok {
switch strVal {
case "320k":
bitrate = 320
case "256k":
bitrate = 256
case "192k":
bitrate = 192
case "128k":
bitrate = 128
default:
return fmt.Errorf("unknown setting bitrate=%s", strVal)
}
} else if intVal, ok = val.(int); ok {
bitrate = intVal
}
}
if val, ok = options["mode"]; ok {
if strVal, ok = val.(string); ok {
switch strVal {
case "lc":
isHEv2 = false
case "hev2":
isHEv2 = true
default:
return fmt.Errorf("unknown setting mode=%s", strVal)
}
}
}
if val, ok = options["format"]; ok {
if strVal, ok = val.(string); ok {
format = strVal
}
}
}
muxingMode := fdkaac.MuxingModeADTS
if format == "adts" {
muxingMode = fdkaac.MuxingModeADTS
} else if format == "mp4" {
muxingMode = fdkaac.MuxingModeRAW
} else if format == "adif" {
muxingMode = fdkaac.MuxingModeADIF
} else {
return fmt.Errorf("unsupported format %s", format)
}
encoder := fdkaac.NewAacEncoder()
if isHEv2 {
err := encoder.InitHEv2(source.Channels, source.SampleRate, bitrate*1024, muxingMode)
if err != nil {
return err
}
} else {
err := encoder.InitLc(source.Channels, source.SampleRate, bitrate*1024, muxingMode)
if err != nil {
return err
}
}
defer encoder.Close()
frameSize := encoder.FrameSize() * encoder.Channels()
if format == "mp4" {
init := mp4.CreateEmptyInit()
init.AddEmptyTrack(uint32(source.SampleRate), "audio", "en")
trackId := init.Moov.Mvhd.NextTrackID - 1
trak := init.Moov.Trak
objType := aac_adts.AAClc
if isHEv2 {
objType = aac_adts.HEAACv2
}
{
stsd := trak.Mdia.Minf.Stbl.Stsd
asc := &aac_adts.AudioSpecificConfig{
ObjectType: byte(objType),
ChannelConfiguration: byte(source.Channels),
SamplingFrequency: source.SampleRate,
ExtensionFrequency: 0,
SBRPresentFlag: false,
PSPresentFlag: false,
}
switch objType {
case aac_adts.HEAACv1:
asc.ExtensionFrequency = 2 * source.SampleRate
asc.SBRPresentFlag = true
case aac_adts.HEAACv2:
asc.ExtensionFrequency = 2 * source.SampleRate
asc.SBRPresentFlag = true
asc.ChannelConfiguration = 1
asc.PSPresentFlag = true
}
buf := &bytes.Buffer{}
err := asc.Encode(buf)
if err != nil {
return err
}
ascBytes := buf.Bytes()
esds := mp4.CreateEsdsBox(ascBytes)
mp4a := mp4.CreateAudioSampleEntryBox("mp4a",
uint16(asc.ChannelConfiguration),
16, uint16(source.SampleRate), esds)
stsd.AddChild(mp4a)
}
init.Encode(writer)
var seqNumber uint32
var packetsWritten uint64
var outputBuffer [][]byte
segmentDuration := time.Millisecond * 100
outputSegment := func() {
seg := mp4.NewMediaSegment()
frag, _ := mp4.CreateFragment(seqNumber, trackId)
seg.AddFragment(frag)
for _, b := range outputBuffer {
frag.AddFullSampleToTrack(mp4.FullSample{
Sample: mp4.Sample{
Dur: uint32(frameSize),
Size: uint32(len(b)),
},
DecodeTime: uint64(frameSize) * packetsWritten,
Data: b,
}, trackId)
packetsWritten++
}
seg.Encode(writer)
seqNumber++
outputBuffer = nil
}
outputPacket := func(packet []byte) {
outputBuffer = append(outputBuffer, packet)
if time.Duration(float64(time.Second)*(float64(frameSize*len(outputBuffer))/float64(source.SampleRate))) >= segmentDuration {
outputSegment()
}
}
var buffer []int16
for block := range source.Blocks {
buffer = append(buffer, cgo.Float32ToInt16(block)...)
for len(buffer) >= frameSize {
sl := buffer[:frameSize]
buf, err := encoder.Encode(unsafe.Slice((*byte)(unsafe.Pointer(&sl[0])), len(sl)*2))
if err != nil {
return err
}
if len(buf) > 0 {
outputPacket(buf)
}
buffer = buffer[frameSize:]
}
}
if len(buffer) > 0 {
//pad
buffer = append(buffer, make([]int16, frameSize-len(buffer))...)
buf, err := encoder.Encode(unsafe.Slice((*byte)(unsafe.Pointer(&buffer[0])), len(buffer)*2))
if err != nil {
return err
}
if len(buf) > 0 {
outputPacket(buf)
}
}
//Do flush
for {
buf, err := encoder.Flush()
if err != nil {
return err
}
if len(buf) > 0 {
outputPacket(buf)
} else {
break
}
}
if len(outputBuffer) > 0 {
outputSegment()
}
} else {
var buffer []int16
for block := range source.Blocks {
buffer = append(buffer, cgo.Float32ToInt16(block)...)
for len(buffer) >= frameSize {
sl := buffer[:frameSize]
buf, err := encoder.Encode(unsafe.Slice((*byte)(unsafe.Pointer(&sl[0])), len(sl)*2))
if err != nil {
return err
}
if len(buf) > 0 {
_, err = writer.Write(buf)
if err != nil {
return err
}
}
buffer = buffer[frameSize:]
}
}
if len(buffer) > 0 {
//pad
buffer = append(buffer, make([]int16, frameSize-len(buffer))...)
buf, err := encoder.Encode(unsafe.Slice((*byte)(unsafe.Pointer(&buffer[0])), len(buffer)*2))
if err != nil {
return err
}
if len(buf) > 0 {
_, err = writer.Write(buf)
if err != nil {
return err
}
}
}
//Do flush
for {
buf, err := encoder.Flush()
if err != nil {
return err
}
if len(buf) > 0 {
_, err = writer.Write(buf)
if err != nil {
return err
}
} else {
break
}
}
}
return nil
}
func (f Format) Identify(peek []byte, extension string) bool {
return extension == "aac" || extension == "adts"
}