mirror of
https://github.com/AlexxIT/go2rtc.git
synced 2025-10-06 16:57:29 +08:00
125 lines
2.8 KiB
Go
125 lines
2.8 KiB
Go
package aac
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"fmt"
|
|
|
|
"github.com/AlexxIT/go2rtc/pkg/bits"
|
|
"github.com/AlexxIT/go2rtc/pkg/core"
|
|
)
|
|
|
|
const (
|
|
TypeAACMain = 1
|
|
TypeAACLC = 2 // Low Complexity
|
|
TypeAACLD = 23 // Low Delay (48000, 44100, 32000, 24000, 22050)
|
|
TypeESCAPE = 31
|
|
TypeAACELD = 39 // Enhanced Low Delay
|
|
|
|
AUTime = 1024
|
|
|
|
// FMTP streamtype=5 - audio stream
|
|
FMTP = "streamtype=5;profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3;config="
|
|
)
|
|
|
|
var sampleRates = [16]uint32{
|
|
96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350,
|
|
0, 0, 0, // protection from request sampleRates[15]
|
|
}
|
|
|
|
func ConfigToCodec(conf []byte) *core.Codec {
|
|
// https://en.wikipedia.org/wiki/MPEG-4_Part_3#MPEG-4_Audio_Object_Types
|
|
rd := bits.NewReader(conf)
|
|
|
|
codec := &core.Codec{
|
|
FmtpLine: FMTP + hex.EncodeToString(conf),
|
|
PayloadType: core.PayloadTypeRAW,
|
|
}
|
|
|
|
objType := rd.ReadBits(5)
|
|
if objType == TypeESCAPE {
|
|
objType = 32 + rd.ReadBits(6)
|
|
}
|
|
|
|
switch objType {
|
|
case TypeAACLC, TypeAACLD, TypeAACELD:
|
|
codec.Name = core.CodecAAC
|
|
default:
|
|
codec.Name = fmt.Sprintf("AAC-%X", objType)
|
|
}
|
|
|
|
if sampleRateIdx := rd.ReadBits8(4); sampleRateIdx < 0x0F {
|
|
codec.ClockRate = sampleRates[sampleRateIdx]
|
|
} else {
|
|
codec.ClockRate = rd.ReadBits(24)
|
|
}
|
|
|
|
codec.Channels = rd.ReadBits16(4)
|
|
|
|
return codec
|
|
}
|
|
|
|
func DecodeConfig(b []byte) (objType, sampleFreqIdx, channels byte, sampleRate uint32) {
|
|
rd := bits.NewReader(b)
|
|
|
|
objType = rd.ReadBits8(5)
|
|
if objType == 0b11111 {
|
|
objType = 32 + rd.ReadBits8(6)
|
|
}
|
|
|
|
sampleFreqIdx = rd.ReadBits8(4)
|
|
if sampleFreqIdx == 0b1111 {
|
|
sampleRate = rd.ReadBits(24)
|
|
}
|
|
|
|
channels = rd.ReadBits8(4)
|
|
return
|
|
}
|
|
|
|
func EncodeConfig(objType byte, sampleRate uint32, channels byte, shortFrame bool) []byte {
|
|
wr := bits.NewWriter(nil)
|
|
|
|
if objType < TypeESCAPE {
|
|
wr.WriteBits8(objType, 5)
|
|
} else {
|
|
wr.WriteBits8(TypeESCAPE, 5)
|
|
wr.WriteBits8(objType-32, 6)
|
|
}
|
|
|
|
i := indexUint32(sampleRates[:], sampleRate)
|
|
if i >= 0 {
|
|
wr.WriteBits8(byte(i), 4)
|
|
} else {
|
|
wr.WriteBits8(0xF, 4)
|
|
wr.WriteBits(sampleRate, 24)
|
|
}
|
|
|
|
wr.WriteBits8(channels, 4)
|
|
|
|
switch objType {
|
|
case TypeAACLD:
|
|
// https://github.com/FFmpeg/FFmpeg/blob/67d392b97941bb51fb7af3a3c9387f5ab895fa46/libavcodec/aacdec_template.c#L841
|
|
wr.WriteBool(shortFrame)
|
|
wr.WriteBit(0) // dependsOnCoreCoder
|
|
wr.WriteBit(0) // extension_flag
|
|
wr.WriteBits8(0, 2) // ep_config
|
|
case TypeAACELD:
|
|
// https://github.com/FFmpeg/FFmpeg/blob/67d392b97941bb51fb7af3a3c9387f5ab895fa46/libavcodec/aacdec_template.c#L922
|
|
wr.WriteBool(shortFrame)
|
|
wr.WriteBits8(0, 3) // res_flags
|
|
wr.WriteBit(0) // ldSbrPresentFlag
|
|
wr.WriteBits8(0, 4) // ELDEXT_TERM
|
|
wr.WriteBits8(0, 2) // ep_config
|
|
}
|
|
|
|
return wr.Bytes()
|
|
}
|
|
|
|
func indexUint32(s []uint32, v uint32) int {
|
|
for i := range s {
|
|
if v == s[i] {
|
|
return i
|
|
}
|
|
}
|
|
return -1
|
|
}
|