Files
go2rtc/pkg/pcm/pcm.go
T
2025-04-24 21:23:16 +03:00

221 lines
4.1 KiB
Go

package pcm
import (
"math"
"github.com/AlexxIT/go2rtc/pkg/core"
)
func ceil(x float32) int {
d, fract := math.Modf(float64(x))
if fract == 0.0 {
return int(d)
}
return int(d) + 1
}
func Downsample(k float32) func([]int16) []int16 {
var sampleN, sampleSum float32
return func(src []int16) (dst []int16) {
var i int
dst = make([]int16, ceil((float32(len(src))+sampleN)/k))
for _, sample := range src {
sampleSum += float32(sample)
sampleN++
if sampleN >= k {
dst[i] = int16(sampleSum / k)
i++
sampleSum = 0
sampleN -= k
}
}
return
}
}
func Upsample(k float32) func([]int16) []int16 {
var sampleN float32
return func(src []int16) (dst []int16) {
var i int
dst = make([]int16, ceil(k*float32(len(src))))
for _, sample := range src {
sampleN += k
for sampleN > 0 {
dst[i] = sample
i++
sampleN -= 1
}
}
return
}
}
func FlipEndian(src []byte) (dst []byte) {
var i, j int
n := len(src)
dst = make([]byte, n)
for i < n {
x := src[i]
i++
dst[j] = src[i]
j++
i++
dst[j] = x
j++
}
return
}
func Transcode(dst, src *core.Codec) func([]byte) []byte {
var reader func([]byte) []int16
var writer func([]int16) []byte
var filters []func([]int16) []int16
switch src.Name {
case core.CodecPCML:
reader = func(src []byte) (dst []int16) {
var i, j int
n := len(src)
dst = make([]int16, n/2)
for i < n {
lo := src[i]
i++
hi := src[i]
i++
dst[j] = int16(hi)<<8 | int16(lo)
j++
}
return
}
case core.CodecPCM:
reader = func(src []byte) (dst []int16) {
var i, j int
n := len(src)
dst = make([]int16, n/2)
for i < n {
hi := src[i]
i++
lo := src[i]
i++
dst[j] = int16(hi)<<8 | int16(lo)
j++
}
return
}
case core.CodecPCMU:
reader = func(src []byte) (dst []int16) {
var i int
dst = make([]int16, len(src))
for _, sample := range src {
dst[i] = PCMUtoPCM(sample)
i++
}
return
}
case core.CodecPCMA:
reader = func(src []byte) (dst []int16) {
var i int
dst = make([]int16, len(src))
for _, sample := range src {
dst[i] = PCMAtoPCM(sample)
i++
}
return
}
}
if src.Channels > 1 {
filters = append(filters, Downsample(float32(src.Channels)))
}
if src.ClockRate > dst.ClockRate {
filters = append(filters, Downsample(float32(src.ClockRate)/float32(dst.ClockRate)))
} else if src.ClockRate < dst.ClockRate {
filters = append(filters, Upsample(float32(dst.ClockRate)/float32(src.ClockRate)))
}
if dst.Channels > 1 {
filters = append(filters, Upsample(float32(dst.Channels)))
}
switch dst.Name {
case core.CodecPCML:
writer = func(src []int16) (dst []byte) {
var i int
dst = make([]byte, len(src)*2)
for _, sample := range src {
dst[i] = byte(sample)
i++
dst[i] = byte(sample >> 8)
i++
}
return
}
case core.CodecPCM:
writer = func(src []int16) (dst []byte) {
var i int
dst = make([]byte, len(src)*2)
for _, sample := range src {
dst[i] = byte(sample >> 8)
i++
dst[i] = byte(sample)
i++
}
return
}
case core.CodecPCMU:
writer = func(src []int16) (dst []byte) {
var i int
dst = make([]byte, len(src))
for _, sample := range src {
dst[i] = PCMtoPCMU(sample)
i++
}
return
}
case core.CodecPCMA:
writer = func(src []int16) (dst []byte) {
var i int
dst = make([]byte, len(src))
for _, sample := range src {
dst[i] = PCMtoPCMA(sample)
i++
}
return
}
}
return func(b []byte) []byte {
samples := reader(b)
for _, filter := range filters {
samples = filter(samples)
}
return writer(samples)
}
}
func ConsumerCodecs() []*core.Codec {
return []*core.Codec{
{Name: core.CodecPCML},
{Name: core.CodecPCM},
{Name: core.CodecPCMA},
{Name: core.CodecPCMU},
}
}
func ProducerCodecs() []*core.Codec {
return []*core.Codec{
{Name: core.CodecPCML, ClockRate: 16000},
{Name: core.CodecPCM, ClockRate: 16000},
{Name: core.CodecPCML, ClockRate: 8000},
{Name: core.CodecPCM, ClockRate: 8000},
{Name: core.CodecPCMA, ClockRate: 8000},
{Name: core.CodecPCMU, ClockRate: 8000},
{Name: core.CodecPCML, ClockRate: 22050}, // wyoming-snd-external
}
}