You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 00:54:54