Files
AskaEth 4a2d276881
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
feat(capability): 转码能力包(vendor 纯 Go AMR-NB 解码器 + 黄金向量逐样本对照 + 8 哨兵 + 注册面)
2026-10-05 13:02:28 +08:00

238 lines
5.5 KiB
Go

package amrnb
import "errors"
// RFC 4867 RTP payload framing for AMR-NB. Two formats are supported:
//
// - octet-aligned (octet-align=1): every field starts on an octet boundary.
// Layout: [CMR(1 byte)] [ToC byte per frame] [speech bytes per frame].
// - bandwidth-efficient: fields are bit-packed with no padding.
// Layout: CMR(4 bits) then, per frame, ToC(6 bits) then the speech bits.
//
// Only the fields VoiceBlender needs are modelled. Interleaving and the
// CRC/robust-sorting options are not used (they are negotiated off by default
// and we never offer them).
// noCMR is the "no mode request" CMR value (15) emitted when we have no
// preference to signal to the peer.
const noCMR = 15
// frame is one decoded ToC entry plus its raw speech bits.
type frame struct {
ft int // frame type (FT)
q bool // quality bit (Q): true = good frame
data []byte // speech bits, octet-aligned, left-justified in the final byte
}
var (
errShortPayload = errors.New("amrnb: payload too short")
errBadFrameType = errors.New("amrnb: unknown frame type")
)
// packPayloadOctet builds an octet-aligned payload carrying the given frames
// with the supplied CMR (use noCMR for none).
func packPayloadOctet(cmr int, frames []frame) []byte {
out := []byte{byte(cmr&0x0F) << 4}
for i, f := range frames {
last := i == len(frames)-1
toc := byte(f.ft&0x0F) << 3
if !last {
toc |= 0x80 // F bit: more frames follow
}
if f.q {
toc |= 0x04 // Q bit
}
out = append(out, toc)
}
for _, f := range frames {
nb, _ := frameBytesByFT(f.ft)
buf := make([]byte, nb)
copy(buf, f.data)
out = append(out, buf...)
}
return out
}
// unpackPayloadOctet parses an octet-aligned payload into its frames and CMR.
func unpackPayloadOctet(p []byte) (cmr int, frames []frame, err error) {
if len(p) < 1 {
return 0, nil, errShortPayload
}
cmr = int(p[0] >> 4)
idx := 1
// Read ToC bytes until the F bit clears.
var tocs []byte
for {
if idx >= len(p) {
return 0, nil, errShortPayload
}
toc := p[idx]
idx++
tocs = append(tocs, toc)
if toc&0x80 == 0 {
break
}
}
for _, toc := range tocs {
ft := int(toc>>3) & 0x0F
q := toc&0x04 != 0
nb, ok := frameBytesByFT(ft)
if !ok {
return 0, nil, errBadFrameType
}
if idx+nb > len(p) {
return 0, nil, errShortPayload
}
data := make([]byte, nb)
copy(data, p[idx:idx+nb])
idx += nb
frames = append(frames, frame{ft: ft, q: q, data: data})
}
return cmr, frames, nil
}
// packPayloadBandwidthEfficient builds a bandwidth-efficient payload.
func packPayloadBandwidthEfficient(cmr int, frames []frame) []byte {
w := &bitWriter{}
w.writeBits(uint32(cmr&0x0F), 4)
for i, f := range frames {
last := i == len(frames)-1
fBit := uint32(0)
if !last {
fBit = 1
}
w.writeBits(fBit, 1)
w.writeBits(uint32(f.ft&0x0F), 4)
q := uint32(0)
if f.q {
q = 1
}
w.writeBits(q, 1)
}
for _, f := range frames {
bits, _ := frameBitsByFT(f.ft)
w.writeBitsFromBytes(f.data, bits)
}
return w.bytes()
}
// unpackPayloadBandwidthEfficient parses a bandwidth-efficient payload.
func unpackPayloadBandwidthEfficient(p []byte) (cmr int, frames []frame, err error) {
r := &bitReader{data: p}
c, ok := r.readBits(4)
if !ok {
return 0, nil, errShortPayload
}
cmr = int(c)
type toc struct {
ft int
q bool
}
var tocs []toc
for {
fBit, ok := r.readBits(1)
if !ok {
return 0, nil, errShortPayload
}
ft, ok := r.readBits(4)
if !ok {
return 0, nil, errShortPayload
}
qBit, ok := r.readBits(1)
if !ok {
return 0, nil, errShortPayload
}
if _, valid := frameBitsByFT(int(ft)); !valid {
return 0, nil, errBadFrameType
}
tocs = append(tocs, toc{ft: int(ft), q: qBit == 1})
if fBit == 0 {
break
}
}
for _, t := range tocs {
bits, _ := frameBitsByFT(t.ft)
data, ok := r.readBitsToBytes(bits)
if !ok {
return 0, nil, errShortPayload
}
frames = append(frames, frame{ft: t.ft, q: t.q, data: data})
}
return cmr, frames, nil
}
// bitWriter accumulates bits MSB-first into a byte slice.
type bitWriter struct {
buf []byte
cur byte
nimb int // number of bits filled in cur
}
func (w *bitWriter) writeBit(b uint32) {
w.cur |= byte(b&1) << uint(7-w.nimb)
w.nimb++
if w.nimb == 8 {
w.buf = append(w.buf, w.cur)
w.cur = 0
w.nimb = 0
}
}
func (w *bitWriter) writeBits(v uint32, n int) {
for i := n - 1; i >= 0; i-- {
w.writeBit(v >> uint(i))
}
}
// writeBitsFromBytes writes the first n bits of data, MSB-first.
func (w *bitWriter) writeBitsFromBytes(data []byte, n int) {
for i := 0; i < n; i++ {
bit := uint32(0)
if byteIdx := i / 8; byteIdx < len(data) {
bit = uint32(data[byteIdx]>>uint(7-i%8)) & 1
}
w.writeBit(bit)
}
}
func (w *bitWriter) bytes() []byte {
out := w.buf
if w.nimb > 0 {
out = append(out, w.cur)
}
return out
}
// bitReader reads bits MSB-first from a byte slice.
type bitReader struct {
data []byte
pos int // bit position
}
func (r *bitReader) readBits(n int) (uint32, bool) {
var v uint32
for i := 0; i < n; i++ {
if r.pos >= len(r.data)*8 {
return 0, false
}
bit := uint32(r.data[r.pos/8]>>uint(7-r.pos%8)) & 1
v = (v << 1) | bit
r.pos++
}
return v, true
}
// readBitsToBytes reads n bits and returns them left-justified in a byte slice.
func (r *bitReader) readBitsToBytes(n int) ([]byte, bool) {
out := make([]byte, (n+7)/8)
for i := 0; i < n; i++ {
if r.pos >= len(r.data)*8 {
return nil, false
}
bit := r.data[r.pos/8] >> uint(7-r.pos%8) & 1
out[i/8] |= bit << uint(7-i%8)
r.pos++
}
return out, true
}