技术咨询:Discord多用户Opus流合并及缓冲区转单流问题
合并Discord多用户Opus流并转换为MP3的解决方案
问题1:如何合并多个Opus流?
Opus是带状态的有损压缩编码,无法直接拼接多个独立用户的Opus流,必须走解码→混音→编码/转MP3的流程:
解码Opus到PCM
Discord语音流固定为48kHz采样率、单声道,单帧Opus对应960个PCM样本(20ms时长)。为每个用户的Opus数据包单独解码:import opuslib # 初始化匹配Discord参数的解码器 decoder = opuslib.Decoder(48000, 1) # 解码单个Opus数据包 pcm_data = decoder.decode(opus_packet, frame_size=960)多PCM流混音
单声道混音核心是对齐时间轴,叠加对应位置的样本值,需先转32位整数避免溢出:import numpy as np # 主缓冲区用int32存储,防止叠加溢出 main_mixed_pcm = np.array([], dtype=np.int32) def mix_user_pcm(main_buffer, user_pcm, start_sample_offset): # 将用户PCM字节转成int32数组 user_pcm_np = np.frombuffer(user_pcm, dtype=np.int16).astype(np.int32) # 扩展主缓冲区以容纳新片段 required_length = start_sample_offset + len(user_pcm_np) if len(main_buffer) < required_length: main_buffer = np.pad(main_buffer, (0, required_length - len(main_buffer)), mode="constant") # 叠加样本并限制在16位音频范围 main_buffer[start_sample_offset:start_sample_offset+len(user_pcm_np)] += user_pcm_np main_buffer = np.clip(main_buffer, -32768, 32767) return main_buffer关键要记录每个用户音频的时间偏移,比如用户在第5秒发言,对应的样本偏移是
5 * 48000,确保混音时时间对齐。转成MP3文件
用pydub快速将混音后的PCM转成MP3:from pydub import AudioSegment # 将int32缓冲区转成int16字节数据 final_pcm = main_mixed_pcm.astype(np.int16).tobytes() # 创建音频对象并导出 merged_audio = AudioSegment( data=final_pcm, sample_width=2, frame_rate=48000, channels=1 ) merged_audio.export("combined_voice.mp3", format="mp3")
问题2:是否可将缓冲区存入列表后转换为单个Opus流?
可以,但不能直接拼接原始Opus数据包(每个用户的Opus流有独立编码器状态,拼接后播放会杂音、错位),正确流程是:
- 收集所有用户的Opus数据包,同时记录每个包对应的时间偏移
- 按问题1的步骤,将所有Opus解码、混音成统一PCM流
- 用新的Opus编码器将混音后的PCM编码为单个Opus流:
此时import opuslib # 初始化匹配Discord参数的编码器 encoder = opuslib.Encoder(48000, 1, opuslib.APPLICATION_AUDIO) single_opus_frames = [] # 按960样本分割PCM,编码为连续Opus包 for i in range(0, len(main_mixed_pcm), 960): pcm_frame = main_mixed_pcm[i:i+960].astype(np.int16).tobytes() opus_packet = encoder.encode(pcm_frame, 960) single_opus_frames.append(opus_packet)single_opus_frames就是可正常播放的单个Opus流数据包列表,可直接保存或后续处理。
内容的提问来源于stack exchange,提问作者Ali Eren
相关产品推荐
相关产品推荐

