使用FFmpeg合并多音频至视频后音量渐变增大,如何修复?
修复FFmpeg合并多段音频后音量逐渐增大的问题
问题背景
使用Node.js调用FFmpeg将多段音频合并到视频时,出现音量从开头到结尾逐渐增大的异常,原视频本身无此问题。尝试给单个音频添加dynaudnorm滤镜未解决,在合并滤镜末尾添加该滤镜导致会话失败。
原代码片段
const command = ffmpeg(); const mp4Path = path.join(__dirname, '..', '..', 'temp', `q-video-${new Date().getTime()}.mp4`); fs.writeFileSync(mp4Path, videoBuff); console.log('mp4 file created at: ', mp4Path); // Set the video stream as the input for ffmpeg command.input(mp4Path); const mp3Paths = []; for (let i = 0; i < audios.length; i++) { const audio = audios[i]; const mp3Path = path.join(__dirname, '..', '..', 'temp', `q-audio-${new Date().getTime()}-${i}.mp3`); mp3Paths.push(mp3Path); fs.writeFileSync(mp3Path, audio.questionBuf); console.log('mp3 file created at: ', mp3Path); // Set the audio stream as the input for ffmpeg command.input(mp3Path); } // ------- // ChatGPT take 1 const audioTags = []; const audioFilters = audios.map((audio, index) => { const startTime = audio.start_at; // Replace with your logic to calculate start time const endTime = audio.end_at; // Replace with your logic to calculate end time audioTags.push(`[delayed${index}]`); // Working // return `[${index + 1}:a]atrim=start=0:end=${(endTime - startTime) / 1000},adelay=${startTime}[delayed${index}]`; return `[${index + 1}:a]dynaudnorm=p=0.9:m=100:s=5,atrim=start=0:end=${(endTime - startTime) / 1000},adelay=${startTime}[delayed${index}]`; }); // Concatenate the delayed audio streams const concatFilter = audioFilters.join(';'); // Mix the concatenated audio streams const mixFilter = `${concatFilter};[0:a]${audioTags.join('')}amix=inputs=${audios.length + 1}:duration=first:dropout_transition=2[out]`; // Set the complex filter for ffmpeg command.complexFilter([mixFilter]); // 后续视频滤镜、输入输出配置及事件处理代码省略
问题根源
- 给单个音频片段添加
dynaudnorm是错误的:该滤镜会单独归一化每段音频的音量,但合并时后期叠加的音频片段越来越多,总音量自然累加上升。 amix的dropout_transition=2参数会导致音频片段结束时产生2秒的增益过渡,进一步加剧音量逐渐增大的问题。
修复方案
1. 调整滤镜链结构:先混合,再全局归一化
移除单音频的dynaudnorm,先完成所有音频(含原视频音频)的延迟、裁剪、混合,再对最终混合音频做全局音量归一化,确保整个视频音量一致。
2. 修改关键滤镜代码
替换原有的音频滤镜生成逻辑:
const audioTags = []; const audioFilters = audios.map((audio, index) => { const startTime = audio.start_at; const endTime = audio.end_at; audioTags.push(`[delayed${index}]`); // 移除单音频的dynaudnorm,仅保留裁剪和延迟逻辑 return `[${index + 1}:a]atrim=start=0:end=${(endTime - startTime) / 1000},adelay=${startTime}[delayed${index}]`; }); const concatFilter = audioFilters.join(';'); // 先混合所有音频,再对混合结果做全局音量归一化 // 方案1:使用dynaudnorm做动态归一化 const mixFilter = `${concatFilter};[0:a]${audioTags.join('')}amix=inputs=${audios.length + 1}:duration=first:dropout_transition=0[mixed];[mixed]dynaudnorm=p=0.9:m=100:s=5[out]`; // 方案2:推荐使用loudnorm(EBU R128标准,标准化到目标响度,更专业) // const mixFilter = `${concatFilter};[0:a]${audioTags.join('')}amix=inputs=${audios.length + 1}:duration=first:dropout_transition=0[mixed];[mixed]loudnorm=I=-16:LRA=11:TP=-1.5[out]`; // 设置复杂滤镜 command.complexFilter([mixFilter]);
3. 参数说明
dropout_transition=0:关闭音频片段结束时的过渡增益,避免音量异常波动。dynaudnorm参数:p=0.9(目标峰值)、m=100(最大增益)、s=5(滑动窗口大小),可根据实际需求调整。loudnorm参数:I=-16(目标集成响度,适合流媒体)、LRA=11(响度范围)、TP=-1.5(峰值阈值),符合行业标准。
4. 额外优化(可选)
如果原视频音频本身音量偏低,可以单独对其做增益调整:
// 在混合前给原视频音频增加音量(示例:增益1.5倍) const mixFilter = `[0:a]volume=1.5[video_audio];${concatFilter};[video_audio]${audioTags.join('')}amix=inputs=${audios.length + 1}:duration=first:dropout_transition=0[mixed];[mixed]dynaudnorm=p=0.9:m=100:s=5[out]`;
内容的提问来源于stack exchange,提问作者Terry Windwalker
相关产品推荐
相关产品推荐

