mirror of https://github.com/jetkvm/kvm.git
Fix audio capture to force Opus-compatible sample rates
ALSA now forces the configured sample rate (default 48kHz) instead of auto-detecting the source rate. This prevents Opus encoder initialization failures when HDMI sources output 44.1kHz audio, which Opus doesn't support. Changes: - Use snd_pcm_hw_params_set_rate() to force exact rate (48kHz by default) - ALSA performs software resampling if hardware rate differs - Update valid rates to Opus-compatible only (8k, 12k, 16k, 24k, 48k) - Remove auto-adaptation logic that caused Opus failures with 44.1kHz This ensures audio capture works reliably with any HDMI source rate.
This commit is contained in:
parent
fba4eabf3b
commit
a6cbf20f66
4
audio.go
4
audio.go
|
|
@ -79,8 +79,8 @@ func getAudioConfig() audio.AudioConfig {
|
||||||
cfg.BufferPeriods = uint8(periods)
|
cfg.BufferPeriods = uint8(periods)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate and apply sample rate using a map for valid rates
|
// Opus-compatible rates only: 8k, 12k, 16k, 24k, 48k
|
||||||
validRates := map[int]bool{32000: true, 44100: true, 48000: true, 96000: true}
|
validRates := map[int]bool{8000: true, 12000: true, 16000: true, 24000: true, 48000: true}
|
||||||
if validRates[config.AudioSampleRate] {
|
if validRates[config.AudioSampleRate] {
|
||||||
cfg.SampleRate = uint32(config.AudioSampleRate)
|
cfg.SampleRate = uint32(config.AudioSampleRate)
|
||||||
} else if config.AudioSampleRate != 0 {
|
} else if config.AudioSampleRate != 0 {
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,7 @@ type Config struct {
|
||||||
AudioDTXEnabled bool `json:"audio_dtx_enabled"`
|
AudioDTXEnabled bool `json:"audio_dtx_enabled"`
|
||||||
AudioFECEnabled bool `json:"audio_fec_enabled"`
|
AudioFECEnabled bool `json:"audio_fec_enabled"`
|
||||||
AudioBufferPeriods int `json:"audio_buffer_periods"` // 2-24
|
AudioBufferPeriods int `json:"audio_buffer_periods"` // 2-24
|
||||||
AudioSampleRate int `json:"audio_sample_rate"` // Hz (32000, 44100, 48000)
|
AudioSampleRate int `json:"audio_sample_rate"` // Hz (Opus: 8k, 12k, 16k, 24k, 48k)
|
||||||
AudioPacketLossPerc int `json:"audio_packet_loss_perc"` // 0-100
|
AudioPacketLossPerc int `json:"audio_packet_loss_perc"` // 0-100
|
||||||
NativeMaxRestart uint `json:"native_max_restart_attempts"`
|
NativeMaxRestart uint `json:"native_max_restart_attempts"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
* Key features:
|
* Key features:
|
||||||
* - ARM NEON SIMD optimization for all audio operations
|
* - ARM NEON SIMD optimization for all audio operations
|
||||||
* - Opus in-band FEC for packet loss resilience
|
* - Opus in-band FEC for packet loss resilience
|
||||||
* - S16_LE stereo, 20ms frames, auto-adapts to source rate (32/44.1/48/96 kHz)
|
* - S16_LE stereo, 20ms frames at 48kHz (ALSA resamples non-48kHz sources)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <alsa/asoundlib.h>
|
#include <alsa/asoundlib.h>
|
||||||
|
|
@ -376,28 +376,23 @@ static int configure_alsa_device(snd_pcm_t *handle, const char *device_name, uin
|
||||||
err = snd_pcm_hw_params_set_channels(handle, params, num_channels);
|
err = snd_pcm_hw_params_set_channels(handle, params, num_channels);
|
||||||
if (err < 0) return err;
|
if (err < 0) return err;
|
||||||
|
|
||||||
unsigned int requested_rate = sample_rate;
|
// Force exact rate for Opus compatibility (ALSA resamples if hardware differs)
|
||||||
|
err = snd_pcm_hw_params_set_rate(handle, params, sample_rate, 0);
|
||||||
|
if (err < 0) {
|
||||||
|
fprintf(stderr, "WARNING: %s: Failed to set %u Hz: %s, falling back to nearest\n",
|
||||||
|
device_name, sample_rate, snd_strerror(err));
|
||||||
|
fflush(stderr);
|
||||||
|
|
||||||
unsigned int actual_rate = sample_rate;
|
unsigned int actual_rate = sample_rate;
|
||||||
err = snd_pcm_hw_params_set_rate_near(handle, params, &actual_rate, 0);
|
err = snd_pcm_hw_params_set_rate_near(handle, params, &actual_rate, 0);
|
||||||
if (err < 0) return err;
|
if (err < 0) return err;
|
||||||
|
|
||||||
|
fprintf(stderr, "WARNING: %s: Using %u Hz (Opus may fail if non-standard)\n",
|
||||||
|
device_name, actual_rate);
|
||||||
|
fflush(stderr);
|
||||||
|
}
|
||||||
|
|
||||||
uint16_t actual_frame_size = frame_size;
|
uint16_t actual_frame_size = frame_size;
|
||||||
if (actual_rate != requested_rate) {
|
|
||||||
fprintf(stderr, "INFO: %s: Requested sample rate %u Hz, device supports %u Hz (%.1f%% difference)\n",
|
|
||||||
device_name, requested_rate, actual_rate, ((float)actual_rate - requested_rate) / requested_rate * 100.0f);
|
|
||||||
fprintf(stderr, "INFO: %s: Adapting to device rate %u Hz to avoid pitch/speed distortion\n", device_name, actual_rate);
|
|
||||||
fflush(stderr);
|
|
||||||
|
|
||||||
actual_frame_size = (actual_rate * 20) / 1000;
|
|
||||||
if (num_channels == 2) {
|
|
||||||
if (actual_frame_size < 480) actual_frame_size = 480;
|
|
||||||
if (actual_frame_size > 2880) actual_frame_size = 2880;
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, "INFO: %s: Adjusted frame size to %u samples (20ms at %u Hz)\n",
|
|
||||||
device_name, actual_frame_size, actual_rate);
|
|
||||||
fflush(stderr);
|
|
||||||
}
|
|
||||||
|
|
||||||
snd_pcm_uframes_t period_size = actual_frame_size;
|
snd_pcm_uframes_t period_size = actual_frame_size;
|
||||||
if (period_size < 64) period_size = 64;
|
if (period_size < 64) period_size = 64;
|
||||||
|
|
@ -414,9 +409,9 @@ static int configure_alsa_device(snd_pcm_t *handle, const char *device_name, uin
|
||||||
|
|
||||||
unsigned int verified_rate = 0;
|
unsigned int verified_rate = 0;
|
||||||
err = snd_pcm_hw_params_get_rate(params, &verified_rate, 0);
|
err = snd_pcm_hw_params_get_rate(params, &verified_rate, 0);
|
||||||
if (err < 0 || verified_rate != actual_rate) {
|
if (err < 0 || verified_rate != sample_rate) {
|
||||||
fprintf(stderr, "WARNING: %s: Rate verification failed - expected %u Hz, got %u Hz\n",
|
fprintf(stderr, "WARNING: %s: Rate verification failed - expected %u Hz, got %u Hz\n",
|
||||||
device_name, actual_rate, verified_rate);
|
device_name, sample_rate, verified_rate);
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -435,7 +430,7 @@ static int configure_alsa_device(snd_pcm_t *handle, const char *device_name, uin
|
||||||
err = snd_pcm_prepare(handle);
|
err = snd_pcm_prepare(handle);
|
||||||
if (err < 0) return err;
|
if (err < 0) return err;
|
||||||
|
|
||||||
if (actual_rate_out) *actual_rate_out = actual_rate;
|
if (actual_rate_out) *actual_rate_out = sample_rate;
|
||||||
if (actual_frame_size_out) *actual_frame_size_out = actual_frame_size;
|
if (actual_frame_size_out) *actual_frame_size_out = actual_frame_size;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue