fix: 修复 PortAudio 无音频设备检测问题 + 初始化生命周期
1. 重新编译 PortAudio 启用 ALSA 支持(原先编译时未启用任何音频后端, 导致设备数为 0,Pa_GetDefaultInputDevice() 返回 -1) 2. 修复 getDeviceList() 中 Pa_Terminate/Pa_Initialize 顺序错误 (先 Terminate 再 Initialize 会导致状态异常) 3. 使用全局 gPaInitialized 标志管理 PortAudio 生命周期, 避免多次 start/stop 后重复初始化或意外终止 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
6cb73b43a8
commit
ecc79aaeb6
@ -12,6 +12,27 @@ namespace impress {
|
||||
// 预分配缓冲区,避免在实时回调中分配内存
|
||||
static constexpr int kMaxBufferSize = 8192;
|
||||
|
||||
// 全局 PortAudio 初始化状态
|
||||
static bool gPaInitialized = false;
|
||||
|
||||
/** 安全初始化 PortAudio(多次调用不报错) */
|
||||
static bool ensurePaInitialized() {
|
||||
if (gPaInitialized) return true;
|
||||
if (Pa_Initialize() == paNoError) {
|
||||
gPaInitialized = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** 安全终止 PortAudio */
|
||||
static void safePaTerminate() {
|
||||
if (gPaInitialized) {
|
||||
Pa_Terminate();
|
||||
gPaInitialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 回调上下文:独立于 Impl 的 POD 结构,供静态回调使用
|
||||
struct CallbackContext {
|
||||
AudioCapture* owner = nullptr;
|
||||
@ -72,8 +93,10 @@ QStringList AudioCapture::getDeviceList() {
|
||||
QStringList devices;
|
||||
devices << "默认设备";
|
||||
#ifdef HAVE_PORTAUDIO
|
||||
Pa_Terminate(); // 确保未初始化
|
||||
if (Pa_Initialize() == paNoError) {
|
||||
if (!ensurePaInitialized()) {
|
||||
LOG_ERROR(kTag, "PortAudio 初始化失败");
|
||||
return devices;
|
||||
}
|
||||
int count = Pa_GetDeviceCount();
|
||||
for (int i = 0; i < count; ++i) {
|
||||
const PaDeviceInfo* info = Pa_GetDeviceInfo(i);
|
||||
@ -82,8 +105,6 @@ QStringList AudioCapture::getDeviceList() {
|
||||
info->name).arg(info->maxInputChannels).arg(info->defaultSampleRate);
|
||||
}
|
||||
}
|
||||
Pa_Terminate();
|
||||
}
|
||||
#endif
|
||||
return devices;
|
||||
}
|
||||
@ -95,22 +116,20 @@ bool AudioCapture::start(int deviceIndex, int sampleRate, int bufferSizeMs) {
|
||||
}
|
||||
|
||||
#ifdef HAVE_PORTAUDIO
|
||||
if (Pa_Initialize() != paNoError) {
|
||||
if (!ensurePaInitialized()) {
|
||||
LOG_ERROR(kTag, "PortAudio 初始化失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
int devIdx = deviceIndex < 0 ? Pa_GetDefaultInputDevice() : deviceIndex;
|
||||
if (devIdx < 0 || devIdx >= Pa_GetDeviceCount()) {
|
||||
LOG_ERROR(kTag, QString("无效的音频设备索引: %1").arg(deviceIndex));
|
||||
Pa_Terminate();
|
||||
LOG_ERROR(kTag, QString("无效的音频设备索引: %1 (默认设备: %2)").arg(deviceIndex).arg(Pa_GetDefaultInputDevice()));
|
||||
return false;
|
||||
}
|
||||
|
||||
const PaDeviceInfo* devInfo = Pa_GetDeviceInfo(devIdx);
|
||||
if (!devInfo || devInfo->maxInputChannels <= 0) {
|
||||
LOG_ERROR(kTag, "所选设备不是输入设备");
|
||||
Pa_Terminate();
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -131,7 +150,6 @@ bool AudioCapture::start(int deviceIndex, int sampleRate, int bufferSizeMs) {
|
||||
|
||||
if (err != paNoError || !impl_->ctx.stream) {
|
||||
LOG_ERROR(kTag, QString("打开音频流失败: %1").arg(Pa_GetErrorText(err)));
|
||||
Pa_Terminate();
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -140,7 +158,6 @@ bool AudioCapture::start(int deviceIndex, int sampleRate, int bufferSizeMs) {
|
||||
LOG_ERROR(kTag, QString("启动音频流失败: %1").arg(Pa_GetErrorText(err)));
|
||||
Pa_CloseStream(impl_->ctx.stream);
|
||||
impl_->ctx.stream = nullptr;
|
||||
Pa_Terminate();
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -166,7 +183,7 @@ void AudioCapture::stop() {
|
||||
Pa_CloseStream(impl_->ctx.stream);
|
||||
impl_->ctx.stream = nullptr;
|
||||
}
|
||||
Pa_Terminate();
|
||||
safePaTerminate();
|
||||
#endif
|
||||
|
||||
running_ = false;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user