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;
|
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 结构,供静态回调使用
|
// 回调上下文:独立于 Impl 的 POD 结构,供静态回调使用
|
||||||
struct CallbackContext {
|
struct CallbackContext {
|
||||||
AudioCapture* owner = nullptr;
|
AudioCapture* owner = nullptr;
|
||||||
@ -72,8 +93,10 @@ QStringList AudioCapture::getDeviceList() {
|
|||||||
QStringList devices;
|
QStringList devices;
|
||||||
devices << "默认设备";
|
devices << "默认设备";
|
||||||
#ifdef HAVE_PORTAUDIO
|
#ifdef HAVE_PORTAUDIO
|
||||||
Pa_Terminate(); // 确保未初始化
|
if (!ensurePaInitialized()) {
|
||||||
if (Pa_Initialize() == paNoError) {
|
LOG_ERROR(kTag, "PortAudio 初始化失败");
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
int count = Pa_GetDeviceCount();
|
int count = Pa_GetDeviceCount();
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
const PaDeviceInfo* info = Pa_GetDeviceInfo(i);
|
const PaDeviceInfo* info = Pa_GetDeviceInfo(i);
|
||||||
@ -82,8 +105,6 @@ QStringList AudioCapture::getDeviceList() {
|
|||||||
info->name).arg(info->maxInputChannels).arg(info->defaultSampleRate);
|
info->name).arg(info->maxInputChannels).arg(info->defaultSampleRate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Pa_Terminate();
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
return devices;
|
return devices;
|
||||||
}
|
}
|
||||||
@ -95,22 +116,20 @@ bool AudioCapture::start(int deviceIndex, int sampleRate, int bufferSizeMs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_PORTAUDIO
|
#ifdef HAVE_PORTAUDIO
|
||||||
if (Pa_Initialize() != paNoError) {
|
if (!ensurePaInitialized()) {
|
||||||
LOG_ERROR(kTag, "PortAudio 初始化失败");
|
LOG_ERROR(kTag, "PortAudio 初始化失败");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int devIdx = deviceIndex < 0 ? Pa_GetDefaultInputDevice() : deviceIndex;
|
int devIdx = deviceIndex < 0 ? Pa_GetDefaultInputDevice() : deviceIndex;
|
||||||
if (devIdx < 0 || devIdx >= Pa_GetDeviceCount()) {
|
if (devIdx < 0 || devIdx >= Pa_GetDeviceCount()) {
|
||||||
LOG_ERROR(kTag, QString("无效的音频设备索引: %1").arg(deviceIndex));
|
LOG_ERROR(kTag, QString("无效的音频设备索引: %1 (默认设备: %2)").arg(deviceIndex).arg(Pa_GetDefaultInputDevice()));
|
||||||
Pa_Terminate();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PaDeviceInfo* devInfo = Pa_GetDeviceInfo(devIdx);
|
const PaDeviceInfo* devInfo = Pa_GetDeviceInfo(devIdx);
|
||||||
if (!devInfo || devInfo->maxInputChannels <= 0) {
|
if (!devInfo || devInfo->maxInputChannels <= 0) {
|
||||||
LOG_ERROR(kTag, "所选设备不是输入设备");
|
LOG_ERROR(kTag, "所选设备不是输入设备");
|
||||||
Pa_Terminate();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +150,6 @@ bool AudioCapture::start(int deviceIndex, int sampleRate, int bufferSizeMs) {
|
|||||||
|
|
||||||
if (err != paNoError || !impl_->ctx.stream) {
|
if (err != paNoError || !impl_->ctx.stream) {
|
||||||
LOG_ERROR(kTag, QString("打开音频流失败: %1").arg(Pa_GetErrorText(err)));
|
LOG_ERROR(kTag, QString("打开音频流失败: %1").arg(Pa_GetErrorText(err)));
|
||||||
Pa_Terminate();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,7 +158,6 @@ bool AudioCapture::start(int deviceIndex, int sampleRate, int bufferSizeMs) {
|
|||||||
LOG_ERROR(kTag, QString("启动音频流失败: %1").arg(Pa_GetErrorText(err)));
|
LOG_ERROR(kTag, QString("启动音频流失败: %1").arg(Pa_GetErrorText(err)));
|
||||||
Pa_CloseStream(impl_->ctx.stream);
|
Pa_CloseStream(impl_->ctx.stream);
|
||||||
impl_->ctx.stream = nullptr;
|
impl_->ctx.stream = nullptr;
|
||||||
Pa_Terminate();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,7 +183,7 @@ void AudioCapture::stop() {
|
|||||||
Pa_CloseStream(impl_->ctx.stream);
|
Pa_CloseStream(impl_->ctx.stream);
|
||||||
impl_->ctx.stream = nullptr;
|
impl_->ctx.stream = nullptr;
|
||||||
}
|
}
|
||||||
Pa_Terminate();
|
safePaTerminate();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
running_ = false;
|
running_ = false;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user