mirror of
https://github.com/AlexxIT/go2rtc.git
synced 2026-04-22 23:57:20 +08:00
221 lines
4.1 KiB
Go
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
|
|
}
|
|
}
|