Flask+SocketIO定时合并带音频直播流文件报错求助
带音频WebM流合并失败的解决方法
问题场景
开发Flask+JavaScript应用,通过SocketIO每20秒向Flask传输摄像头直播流,需求是首次传输生成视频文件,后续将新流合并到已有文件中。当JS端开启音频配置audio: { echoCancellation: true, noiseSuppression: true }时,使用FFmpeg合并出现Opus解析等错误,仅录制视频时功能正常。
相关代码
JavaScript端代码
async function startCapture() { try { // 访问用户摄像头 stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: { echoCancellation: true, noiseSuppression: true }, }); // 将流绑定到video元素 video.srcObject = stream; // 创建MediaRecorder实例捕获视频片段 recorder = new MediaRecorder(stream); // 处理录制的每个数据块 recorder.ondataavailable = (e) => { const videoBlob = e.data; transmitVideoChunk(videoBlob); chunks.push(e.data); }; // 开始录制 recorder.start(); // 切换按钮状态 startButton.disabled = true; stopButton.disabled = false; // 启动定时传输 startTransmitting(); } catch (error) { console.error('访问摄像头出错:', error); } } function transmitVideoBlob() { const videoBlob = new Blob(chunks, { type: 'video/webm' }); socket.emit('video_data', videoBlob); // 清空片段数组 chunks = []; } // 启动定时传输视频片段 function startTransmitting() { const videoInterval = 20000; // 传输间隔,毫秒 videoIntervalId = setInterval(() => { transmitVideoBlob(); }, videoInterval); }
Flask端处理函数
def create_videos(video_path, filename, new_video_data_blob): chunk_filename = os.path.join(video_path, f"{str(uuid1())}_{filename}") final_filename = os.path.join(video_path, filename) out_final_filename = os.path.join(video_path, "out_" + filename) # 保存当前视频片段到文件 with open(chunk_filename, "wb") as f: print("创建片段文件 ", chunk_filename) f.write(new_video_data_blob) if not os.path.exists(final_filename): # 如果最终文件不存在,直接重命名片段文件 os.rename(chunk_filename, final_filename) else: while not os.path.exists(chunk_filename): time.sleep(0.1) # 如果最终文件存在,用FFmpeg合并当前片段和已有文件 try: subprocess.run( [ "ffmpeg", "-i", f"concat:{final_filename}|{chunk_filename}", "-c", "copy", "-y", out_final_filename, ] ) while not os.path.exists(out_final_filename): time.sleep(0.1) os.remove(final_filename) os.rename(out_final_filename, final_filename) except Exception as e: print(e) # 删除片段文件 os.remove(chunk_filename) return final_filename
报错信息
[NULL @ 0x55e697e8c900] 无效的profile 5。 [webm @ 0x55e697ec3180] 输出流0:0中DTS不单调;上一个:37075,当前:37020;已修改为37075。这可能导致输出文件中的时间戳不正确。 [NULL @ 0x55e697e8d8c0] 解析Opus包头部出错。 最后一条消息重复1次 [NULL @ 0x55e697e8c900] 无效的profile 5。 [NULL @ 0x55e697e8d8c0] 解析Opus包头部出错。
解决方法
1. 改用FFmpeg的demuxer concat方式合并
WebM基于Matroska容器,concat:协议仅支持格式完全一致的文件,而MediaRecorder生成的片段易出现音频参数不匹配、时间戳断裂问题。换用文件列表合并法:
- 生成临时文件列表:
with open("file_list.txt", "w") as f: f.write(f"file '{final_filename}'\n") f.write(f"file '{chunk_filename}'\n") - 修改FFmpeg命令:
完成后删除临时的subprocess.run( [ "ffmpeg", "-f", "concat", "-safe", "0", "-i", "file_list.txt", "-c", "copy", "-y", out_final_filename, ] )file_list.txt。
2. 强制统一MediaRecorder编码参数
指定固定的MIME类型和编码参数,确保所有片段的音视频格式一致:
// 强制VP8视频+Opus音频的WebM格式 const recordOptions = { mimeType: 'video/webm; codecs="vp8,opus"', audioBitsPerSecond: 128000, videoBitsPerSecond: 2500000 }; recorder = new MediaRecorder(stream, recordOptions);
3. 修复时间戳不连续问题
添加-fflags +genpts参数,强制FFmpeg生成正确的时间戳:
subprocess.run( [ "ffmpeg", "-f", "concat", "-safe", "0", "-i", "file_list.txt", "-fflags", "+genpts", "-c", "copy", "-y", out_final_filename, ] )
4. 验证片段文件完整性
合并前检查WebM片段是否有效,避免不完整的文件导致解析错误:
# 检查片段文件是否合法 check_result = subprocess.run( ["ffmpeg", "-v", "error", "-i", chunk_filename, "-f", "null", "-"], capture_output=True, text=True ) if check_result.stderr: print(f"无效片段文件: {chunk_filename}") # 跳过或重新获取该片段
内容的提问来源于stack exchange,提问作者Sanjay Shahi
相关产品推荐
相关产品推荐

