Add PlayFile function to wyoming server
This commit is contained in:
+19
-9
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user