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

使用FFmpeg libavfilter acrossfade仅获首个输入部分输出的问题排查

问题:FFmpeg acrossfade滤镜合并音频块输出不完整

我尝试通过acrossfade滤镜合并两个原始音频块(格式为s16、采样率48000、单声道),但输出仅包含第一个音频块减去淡入淡出样本数的内容,无法得到完整的合并结果。

相关实现代码如下:

滤镜节点创建

avfilter_graph_create_filter(&mediaIn_1,
    avfilter_get_by_name("abuffer"),
    "MediaIn_1",
    "sample_rate=48000:sample_fmt=s16:channel_layout=mono",
    NULL, graph);
avfilter_graph_create_filter(&mediaIn_2,
    avfilter_get_by_name("abuffer"),
    "MediaIn_2",
    "sample_rate=48000:sample_fmt=s16:channel_layout=mono",
    NULL, graph);
avfilter_graph_create_filter(&mediaOut,
    avfilter_get_by_name("abuffersink"),
    "MediaOut",
    NULL, NULL, graph);
avfilter_graph_create_filter(&crossfade,
    avfilter_get_by_name("acrossfade"),
    "crossfade", "nb_samples=6000:c1=tri:c2=tri", NULL, graph);

滤镜图链接

avfilter_link(mediaIn_1, 0, crossfade, 0);
avfilter_link(mediaIn_2, 0, crossfade, 1);
avfilter_link(crossfade, 0, mediaOut, 0);
avfilter_graph_config(graph, NULL);

音频帧创建(以第一个块为例)

frame1 = av_frame_alloc();
frame1->format = AV_SAMPLE_FMT_S16;
frame1->nb_samples = buf1sz / 2;
frame1->sample_rate = 48000;
frame1->ch_layout.order = AV_CHANNEL_ORDER_NATIVE;
frame1->ch_layout.nb_channels = 1;
frame1->ch_layout.u.mask = AV_CH_LAYOUT_MONO;
frame1->ch_layout.opaque = NULL;
frame1->pts = 0;
frame1->duration = buf1sz / 2;
frame1->time_base.num = 1;
frame1->time_base.den = 48000;

av_frame_get_buffer(frame1, 0);
memcpy(frame1->buf[0]->data, buf1, buf1sz);

帧发送与输出获取

// 发送第一个音频块
av_buffersrc_add_frame_flags(mediaIn_1, frame1, 0);
av_buffersrc_add_frame_flags(mediaIn_1, NULL, AV_BUFFERSRC_FLAG_PUSH);

// 发送第二个音频块
av_buffersrc_add_frame_flags(mediaIn_2, frame2, 0);
av_buffersrc_add_frame_flags(mediaIn_2, NULL, AV_BUFFERSRC_FLAG_PUSH);

// 获取输出帧
oframe = av_frame_alloc();
av_buffersink_get_frame_flags(mediaOut, oframe, 0);
// ...处理帧
av_frame_unref(oframe);

输出帧仅包含frame1->nb_samples - 6000个样本,再次调用av_buffersink_get_frame_flags返回AVERROR_EOF,无法获取第二个音频块的内容。


解决方案

问题出在双输入滤镜的帧发送时机和EOF信号的处理逻辑上,acrossfade需要第一个输入的结尾和第二个输入的开头重叠,当前的发送方式会导致滤镜提前终止。

1. 核心错误原因

  • 你先给第一个输入发送完所有帧并标记EOF,此时acrossfade会判定第一个输入已结束,直接输出第一个音频块减去交叉淡入长度的内容后就终止流程,不会等待第二个输入的帧。
  • acrossfade的工作逻辑是:需要第一个输入的最后nb_samples个样本和第二个输入的前nb_samples个样本进行混合,因此必须保证第二个输入的帧在第一个输入的EOF信号发送前就进入滤镜队列。

2. 具体修正步骤

步骤一:调整帧发送顺序,延迟发送EOF

不要先给第一个输入发送NULL帧标记EOF,而是先发送第一个音频块的帧,紧接着发送第二个音频块的帧,最后再给两个输入依次发送EOF信号:

// 先发送第一个音频块的帧
av_buffersrc_add_frame_flags(mediaIn_1, frame1, 0);

// 接着发送第二个音频块的帧
av_buffersrc_add_frame_flags(mediaIn_2, frame2, 0);

// 先给第一个输入发送EOF
av_buffersrc_add_frame_flags(mediaIn_1, NULL, AV_BUFFERSRC_FLAG_PUSH);
// 再给第二个输入发送EOF
av_buffersrc_add_frame_flags(mediaIn_2, NULL, AV_BUFFERSRC_FLAG_PUSH);

步骤二:循环获取输出帧,直到返回EOF

当前只调用了一次av_buffersink_get_frame_flags,但滤镜可能会分多次输出帧,需要循环获取直到返回AVERROR_EOF:

oframe = av_frame_alloc();
int ret;
while ((ret = av_buffersink_get_frame_flags(mediaOut, oframe, 0)) >= 0) {
    // 处理输出帧,比如保存或处理样本
    // ...
    av_frame_unref(oframe);
}
// 检查是否是正常结束
if (ret != AVERROR_EOF) {
    // 处理错误
}
av_frame_free(&oframe);

步骤三:确保帧的PTS和时间线对齐

acrossfade依赖时间线来对齐两个输入,需要将第二个输入的PTS设置为第一个输入的总时长减去交叉淡入的样本数,这样滤镜才能正确计算重叠区域:

// 计算第一个音频块的总样本数(即总时长对应的样本量)
int64_t frame1_total_samples = frame1->nb_samples;
// 第二个帧的PTS设置为第一个帧的总样本数减去交叉淡入样本数
frame2->pts = frame1_total_samples - 6000;
frame2->time_base = frame1->time_base; // 保持时间基准一致

3. 修正后效果

调整后,acrossfade会先缓存第一个输入的最后6000个样本,接着接收第二个输入的前6000个样本进行混合,最终输出内容为:第一个输入的非重叠部分 + 混合过渡部分 + 第二个输入的非重叠部分,总样本数为frame1->nb_samples + frame2->nb_samples - 6000,符合预期。


内容的提问来源于stack exchange,提问作者Alex

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 06:54:51