Add PlayFile function to wyoming server

This commit is contained in:
Alex X
2025-04-24 21:23:16 +03:00
parent 890fd78a6a
commit c50e894a42
8 changed files with 226 additions and 162 deletions
+19 -9
View File
@@ -2,6 +2,7 @@ package pcm
import (
"sync"
"time"
"github.com/AlexxIT/go2rtc/pkg/core"
"github.com/pion/rtp"
@@ -82,18 +83,27 @@ func TranscodeHandler(dst, src *core.Codec, handler core.HandlerFunc) core.Handl
}
}
func BytesPerFrame(codec *core.Codec) byte {
channels := byte(codec.Channels)
if channels == 0 {
channels = 1
}
func BytesPerSample(codec *core.Codec) int {
switch codec.Name {
case core.CodecPCML, core.CodecPCM:
return 2 * channels
return 2
case core.CodecPCMU, core.CodecPCMA:
return channels
return 1
}
return 0
}
func BytesPerFrame(codec *core.Codec) int {
if codec.Channels <= 1 {
return BytesPerSample(codec)
}
return int(codec.Channels) * BytesPerSample(codec)
}
func FramesPerDuration(codec *core.Codec, duration time.Duration) int {
return int(time.Duration(codec.ClockRate) * duration / time.Second)
}
func BytesPerDuration(codec *core.Codec, duration time.Duration) int {
return BytesPerFrame(codec) * FramesPerDuration(codec, duration)
}
+15 -3
View File
@@ -1,13 +1,25 @@
package pcm
import "github.com/AlexxIT/go2rtc/pkg/core"
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, int((float32(len(src))+sampleN)/k))
dst = make([]int16, ceil((float32(len(src))+sampleN)/k))
for _, sample := range src {
sampleSum += float32(sample)
sampleN++
@@ -28,7 +40,7 @@ func Upsample(k float32) func([]int16) []int16 {
return func(src []int16) (dst []int16) {
var i int
dst = make([]int16, int(k*float32(len(src))))
dst = make([]int16, ceil(k*float32(len(src))))
for _, sample := range src {
sampleN += k
for sampleN > 0 {
+96
View File
@@ -0,0 +1,96 @@
package pcm
import (
"io"
"time"
"github.com/AlexxIT/go2rtc/pkg/core"
"github.com/pion/rtp"
)
type ProducerSync struct {
core.Connection
src *core.Codec
rd io.Reader
onClose func()
}
func OpenSync(codec *core.Codec, rd io.Reader) *ProducerSync {
medias := []*core.Media{
{
Kind: core.KindAudio,
Direction: core.DirectionRecvonly,
Codecs: ProducerCodecs(),
},
}
return &ProducerSync{
Connection: core.Connection{
ID: core.NewID(),
FormatName: "pcm",
Medias: medias,
Transport: rd,
},
src: codec,
rd: rd,
}
}
func (p *ProducerSync) OnClose(f func()) {
p.onClose = f
}
func (p *ProducerSync) Start() error {
if len(p.Receivers) == 0 {
return nil
}
var pktSeq uint16
var pktTS uint32 // time in frames
var pktTime time.Duration // time in seconds
t0 := time.Now()
dst := p.Receivers[0].Codec
transcode := Transcode(dst, p.src)
const chunkDuration = 20 * time.Millisecond
chunkBytes := BytesPerDuration(p.src, chunkDuration)
chunkFrames := uint32(FramesPerDuration(dst, chunkDuration))
for {
buf := make([]byte, chunkBytes)
n, _ := io.ReadFull(p.rd, buf)
if n == 0 {
break
}
pkt := &core.Packet{
Header: rtp.Header{
Version: 2,
Marker: true,
SequenceNumber: pktSeq,
Timestamp: pktTS,
},
Payload: transcode(buf[:n]),
}
if d := pktTime - time.Since(t0); d > 0 {
time.Sleep(d)
}
p.Receivers[0].WriteRTP(pkt)
p.Recv += n
pktSeq++
pktTS += chunkFrames
pktTime += chunkDuration
}
if p.onClose != nil {
p.onClose()
}
return nil
}