feat(capability): 转码能力包(vendor 纯 Go AMR-NB 解码器 + 黄金向量逐样本对照 + 8 哨兵 + 注册面)
This commit is contained in:
1 parent
89715d1813
commit
4a2d276881
100 files changed
+11319
No files matched your search
@@ -0,0 +1,237 @@
|
||||
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
|
||||
}
|
||||
Reference in new issue
Block a user