反复点击音频收发按钮触发PyAudio流错误,无法实现双向音频传输
解决PyAudio反复启停音频流导致的[Errno Not input/output stream]错误
反复点击「receive-audio」和「Start」按钮时触发以下错误:
An error occurred: [Errno Not input stream] -9975
An error occurred: [Errno Not output stream] -9974
需要实现双向音频收发,当前代码无法完成,原代码如下:
def send_audio(self): p = pyaudio.PyAudio() while self.is_running: try: self.stream = p.open(format=pyaudio.paInt16, channels=self.CHANNELS, rate=self.RATE, input=True, frames_per_buffer=self.CHUNK) while self.is_running: data = self.stream.read(self.CHUNK) audio_data = np.frombuffer(data, np.int16) self.server_socket.sendall(audio_data) if not self.is_running: break self.stream.stop_stream() self.stream.close() p.terminate() except Exception as e: print("An error occurred:", str(e)) def receive_audio(self): p = pyaudio.PyAudio() while self.is_running_recv: try: self.stream = p.open(format=self.FORMAT, channels=self.CHANNELS, rate=self.RATE, output=True) while self.is_running_recv: data = self.server_socket.recv(self.CHUNK) if not data: break if self.event.is_set(): self.stream.write(data) self.stream.stop_stream() self.stream.close() self.server_socket.close() p.terminate() except Exception as e: print("An error occurred:", str(e))
问题根源
- PyAudio实例反复销毁重建:两个函数每次循环都创建
p = pyaudio.PyAudio(),循环结束就p.terminate(),反复点击会导致音频设备资源未正确释放,新实例无法创建有效流。 - 流对象冲突:两个函数共用
self.stream,旧流未关闭就被新流覆盖,操作无效流引发错误。 - Socket提前关闭:
receive_audio里循环结束就关闭socket,后续点击启动时socket已失效。 - 循环逻辑冗余:外层while内重复创建流,导致同一时间多个流争抢音频设备。
修复方案
调整资源管理逻辑,复用PyAudio实例,分开输入输出流,统一控制启停:
def __init__(self): # 初始化基础参数(根据实际情况调整) self.CHANNELS = 1 self.RATE = 44100 self.CHUNK = 1024 self.FORMAT = pyaudio.paInt16 self.is_running = False self.is_running_recv = False self.event = threading.Event() # 全局复用PyAudio实例 self.p = pyaudio.PyAudio() self.input_stream = None self.output_stream = None self.server_socket = None # 确保socket在初始化或连接方法中创建 def send_audio(self): try: # 仅当流未创建或未激活时初始化输入流 if not self.input_stream or not self.input_stream.is_active(): self.input_stream = self.p.open( format=pyaudio.paInt16, channels=self.CHANNELS, rate=self.RATE, input=True, frames_per_buffer=self.CHUNK ) while self.is_running: data = self.input_stream.read(self.CHUNK) audio_data = np.frombuffer(data, np.int16) self.server_socket.sendall(audio_data) # 停止并清理输入流 if self.input_stream: self.input_stream.stop_stream() self.input_stream.close() self.input_stream = None except Exception as e: print("An error occurred:", str(e)) def receive_audio(self): try: # 仅当流未创建或未激活时初始化输出流 if not self.output_stream or not self.output_stream.is_active(): self.output_stream = self.p.open( format=self.FORMAT, channels=self.CHANNELS, rate=self.RATE, output=True ) while self.is_running_recv: data = self.server_socket.recv(self.CHUNK) if not data: break if self.event.is_set(): self.output_stream.write(data) # 停止并清理输出流 if self.output_stream: self.output_stream.stop_stream() self.output_stream.close() self.output_stream = None except Exception as e: print("An error occurred:", str(e)) # 统一停止所有音频操作并释放资源 def stop_all_audio(self): self.is_running = False self.is_running_recv = False # 清理输入流 if self.input_stream: self.input_stream.stop_stream() self.input_stream.close() self.input_stream = None # 清理输出流 if self.output_stream: self.output_stream.stop_stream() self.output_stream.close() self.output_stream = None # 关闭socket(按需调用,比如程序退出时) if self.server_socket: self.server_socket.close() # 程序退出时调用,销毁PyAudio实例 def cleanup(self): self.stop_all_audio() self.p.terminate()
使用注意事项
- 点击「Start」或「receive-audio」按钮前,先调用
stop_all_audio()停止现有音频流,再设置self.is_running/self.is_running_recv为True并启动对应线程。 - 确保
send_audio和receive_audio在独立线程中运行,避免UI阻塞。 - 如果需要重新连接服务器,重新创建
self.server_socket对象,不要复用已关闭的socket。
内容的提问来源于stack exchange,提问作者Emirhan Erdem
相关产品推荐
相关产品推荐

