Add supported codec check for alsa source

This commit is contained in:
Alex X
2025-03-25 15:52:24 +03:00
parent e1342f06b7
commit 1b41f61247
5 changed files with 63 additions and 41 deletions

View File

@@ -44,7 +44,7 @@ func apiAlsa(w http.ResponseWriter, r *http.Request) {
info, err := dev.Info() info, err := dev.Info()
if err == nil { if err == nil {
formats := formatsToString(dev.ListFormats()) formats := formatsToString(dev.ListFormats())
r1, r2 := dev.RangeSampleRates() r1, r2 := dev.RangeRates()
c1, c2 := dev.RangeChannels() c1, c2 := dev.RangeChannels()
source := &api.Source{ source := &api.Source{
Name: info.ID + " / " + info.Name + " / " + info.SubName, Name: info.ID + " / " + info.Name + " / " + info.SubName,

View File

@@ -3,6 +3,7 @@ package alsa
import ( import (
"github.com/AlexxIT/go2rtc/pkg/alsa/device" "github.com/AlexxIT/go2rtc/pkg/alsa/device"
"github.com/AlexxIT/go2rtc/pkg/core" "github.com/AlexxIT/go2rtc/pkg/core"
"github.com/AlexxIT/go2rtc/pkg/pcm"
"github.com/pion/rtp" "github.com/pion/rtp"
) )
@@ -33,19 +34,25 @@ func newCapture(dev *device.Device) (*Capture, error) {
}, nil }, nil
} }
// readBufferSize - 20ms * 2 bytes per sample * 16000 frames per second * 2 channels / 1000ms per second
const readBufferSize = 20 * 2 * 16000 * 2 / 1000
// bytesPerFrame - 2 bytes per sample * 2 channels
const bytesPerFrame = 2 * 2
func (c *Capture) Start() error { func (c *Capture) Start() error {
if err := c.dev.SetHWParams(device.SNDRV_PCM_FORMAT_S16_LE, 16000, 2); err != nil { dst := c.Medias[0].Codecs[0]
src := &core.Codec{
Name: dst.Name,
ClockRate: c.dev.GetRateNear(dst.ClockRate),
Channels: c.dev.GetChannelsNear(dst.Channels),
}
if err := c.dev.SetHWParams(device.SNDRV_PCM_FORMAT_S16_LE, src.ClockRate, src.Channels); err != nil {
return err return err
} }
transcode := transcodeFunc(dst, src)
frameBytes := int(pcm.BytesPerFrame(src))
var ts uint32 var ts uint32
// readBufferSize for 20ms interval
readBufferSize := 20 * frameBytes * int(src.ClockRate) / 1000
b := make([]byte, readBufferSize) b := make([]byte, readBufferSize)
for { for {
n, err := c.dev.Read(b) n, err := c.dev.Read(b)
@@ -65,25 +72,19 @@ func (c *Capture) Start() error {
Marker: true, Marker: true,
Timestamp: ts, Timestamp: ts,
}, },
Payload: stereoToMono(b[:n]), Payload: transcode(b[:n]),
} }
c.Receivers[0].WriteRTP(pkt) c.Receivers[0].WriteRTP(pkt)
ts += uint32(n / bytesPerFrame) ts += uint32(n / frameBytes)
} }
} }
func stereoToMono(stereo []byte) (mono []byte) { func transcodeFunc(dst, src *core.Codec) func([]byte) []byte {
n := len(stereo) if dst.ClockRate == src.ClockRate && dst.Channels == src.Channels {
mono = make([]byte, n/2) return func(b []byte) []byte {
var i, j int return b
for i < n { }
mono[j] = stereo[i]
j++
i++
mono[j] = stereo[i]
j++
i += 3
} }
return return pcm.Transcode(dst, src)
} }

View File

@@ -75,29 +75,55 @@ func (d *Device) Info() (*Info, error) {
}, nil }, nil
} }
func (d *Device) CheckFormat(format byte) bool {
return d.checkMask(SNDRV_PCM_HW_PARAM_FORMAT, uint32(format))
}
func (d *Device) ListFormats() (formats []byte) { func (d *Device) ListFormats() (formats []byte) {
for i := byte(0); i <= 28; i++ { for i := byte(0); i <= 28; i++ {
if d.checkMask(SNDRV_PCM_HW_PARAM_FORMAT, uint32(i)) { if d.CheckFormat(i) {
formats = append(formats, i) formats = append(formats, i)
} }
} }
return return
} }
func (d *Device) RangeRates() (uint32, uint32) {
return d.getInterval(SNDRV_PCM_HW_PARAM_RATE)
}
func (d *Device) RangeChannels() (byte, byte) { func (d *Device) RangeChannels() (byte, byte) {
minCh, maxCh := d.getInterval(SNDRV_PCM_HW_PARAM_CHANNELS) minCh, maxCh := d.getInterval(SNDRV_PCM_HW_PARAM_CHANNELS)
return byte(minCh), byte(maxCh) return byte(minCh), byte(maxCh)
} }
func (d *Device) RangeSampleRates() (uint32, uint32) { func (d *Device) GetRateNear(rate uint32) uint32 {
return d.getInterval(SNDRV_PCM_HW_PARAM_RATE) r1, r2 := d.RangeRates()
if rate < r1 {
return r1
}
if rate > r2 {
return r2
}
return rate
}
func (d *Device) GetChannelsNear(channels byte) byte {
c1, c2 := d.RangeChannels()
if channels < c1 {
return c1
}
if channels > c2 {
return c2
}
return channels
} }
const bufferSize = 4096 const bufferSize = 4096
func (d *Device) SetHWParams(format byte, sampleRate uint32, channels byte) error { func (d *Device) SetHWParams(format byte, rate uint32, channels byte) error {
d.setInterval(SNDRV_PCM_HW_PARAM_CHANNELS, uint32(channels)) d.setInterval(SNDRV_PCM_HW_PARAM_CHANNELS, uint32(channels))
d.setInterval(SNDRV_PCM_HW_PARAM_RATE, sampleRate) d.setInterval(SNDRV_PCM_HW_PARAM_RATE, rate)
d.setMask(SNDRV_PCM_HW_PARAM_FORMAT, uint32(format)) d.setMask(SNDRV_PCM_HW_PARAM_FORMAT, uint32(format))
//d.setMask(SNDRV_PCM_HW_PARAM_SUBFORMAT, 0) //d.setMask(SNDRV_PCM_HW_PARAM_SUBFORMAT, 0)

View File

@@ -1,6 +1,7 @@
package alsa package alsa
import ( import (
"errors"
"fmt" "fmt"
"net/url" "net/url"
@@ -26,6 +27,11 @@ func Open(rawURL string) (core.Producer, error) {
return nil, err return nil, err
} }
if !dev.CheckFormat(device.SNDRV_PCM_FORMAT_S16_LE) {
_ = dev.Close()
return nil, errors.New("alsa: format S16LE not supported")
}
switch path[len(path)-1] { switch path[len(path)-1] {
case 'p': // playback case 'p': // playback
return newPlayback(dev) return newPlayback(dev)
@@ -34,6 +40,5 @@ func Open(rawURL string) (core.Producer, error) {
} }
_ = dev.Close() _ = dev.Close()
return nil, fmt.Errorf("alsa: unknown path: %s", path) return nil, fmt.Errorf("alsa: unknown path: %s", path)
} }

View File

@@ -43,20 +43,10 @@ func (p *Playback) GetTrack(media *core.Media, codec *core.Codec) (*core.Receive
func (p *Playback) AddTrack(media *core.Media, codec *core.Codec, track *core.Receiver) error { func (p *Playback) AddTrack(media *core.Media, codec *core.Codec, track *core.Receiver) error {
src := track.Codec src := track.Codec
// support probe
if src.Name == core.CodecAny {
src = &core.Codec{
Name: core.CodecPCML,
ClockRate: 16000,
Channels: 2,
}
}
dst := &core.Codec{ dst := &core.Codec{
Name: core.CodecPCML, Name: core.CodecPCML,
ClockRate: src.ClockRate, ClockRate: p.dev.GetRateNear(src.ClockRate),
Channels: 2, Channels: p.dev.GetChannelsNear(src.Channels),
} }
sender := core.NewSender(media, dst) sender := core.NewSender(media, dst)
@@ -74,7 +64,7 @@ func (p *Playback) AddTrack(media *core.Media, codec *core.Codec, track *core.Re
// - Formats: S16_LE, S32_LE // - Formats: S16_LE, S32_LE
// - ClockRates: 8000 - 192000 // - ClockRates: 8000 - 192000
// - Channels: 2 - 10 // - Channels: 2 - 10
err := p.dev.SetHWParams(device.SNDRV_PCM_FORMAT_S16_LE, dst.ClockRate, 2) err := p.dev.SetHWParams(device.SNDRV_PCM_FORMAT_S16_LE, dst.ClockRate, byte(dst.Channels))
if err != nil { if err != nil {
return err return err
} }