av_buffersrc_write_frame / av_buffersrc_add_frame的“invalid argument”

时间:2017-01-30 14:05:10

标签: c++ ffmpeg

我正在尝试使用FFmpeg C api创建一个过滤器来合并两个音频流。尝试遵循此处的代码:Implementing a multiple input filter graph with the Libavfilter library in Android NDK

一切似乎都没问题。

但是,只要我调用av_buffersrc_write_frame(或av_buffersrc_add_frame或av_buffersrc_add_frame_flags,无关紧要),FFmpeg就会报告“无效参数”而不报告任何其他内容 - 这是一个完全没用的错误消息,因为它可能意味着一切。
哪个论点无效?怎么了?没人知道。

我正在初始化图形并“抓住”缓冲区源的上下文以供以后使用,如下所示:

// Alloc filter graph
*filter_graph = avfilter_graph_alloc();
if ((*filter_graph) == NULL) {
    os::log("Error: Cannot allocate filter graph.");
    return AVERROR(ENOMEM);
}

// Building the filter string, ommitted

int result = avfilter_graph_parse2(*filter_graph, filterString.c_str(), &gis, &gos, NULL);
if (result < 0)
{
    char errorBuf[1024];
    av_make_error_string(errorBuf, 1024, result);
    log("Error: Parsing filter string: %s", errorBuf);
    return AVERROR_EXIT;
}

// Configure the graph
result = avfilter_graph_config(*filter_graph, NULL);
if (result < 0)
{
    char errorBuf[1024];
    av_make_error_string(errorBuf, 1024, result);
    log("Error: Configuring filter graph: %s", errorBuf);
    return AVERROR_EXIT;
}

// Get the buffer source and buffer sink contexts
for (unsigned int i = 0; i < (*filter_graph)->nb_filters; ++i) {
    AVFilterContext* filterContext = (*filter_graph)->filters[i];

    // The first two filters should be the abuffers
    std::string name = filterContext->name;
    if (name.find("abuffer") != name.npos && i < 2) {
        inputs[i].buffer_source_context = filterContext;
    }

    // abuffersink is the one we need to get the converted frames from
    if (name.find("abuffersink") != name.npos) {
        *buffer_sink_context = filterContext;
    }
}

初始化绝对没有错误。至少FFmpeg只有这个说,我认为看起来不错:

FFMPEG: [Parsed_abuffer_0 @ 0ddfe840] Setting 'time_base' to value '1/48000'
FFMPEG: [Parsed_abuffer_0 @ 0ddfe840] Setting 'sample_rate' to value '48000'
FFMPEG: [Parsed_abuffer_0 @ 0ddfe840] Setting 'sample_fmt' to value '1'
FFMPEG: [Parsed_abuffer_0 @ 0ddfe840] Setting 'channel_layout' to value '3'
FFMPEG: [Parsed_abuffer_0 @ 0ddfe840] Setting 'channels' to value '2'
FFMPEG: [Parsed_abuffer_0 @ 0ddfe840] tb:1/48000 samplefmt:s16 samplerate:48000 chlayout:3
FFMPEG: [Parsed_abuffer_1 @ 0ddfe500] Setting 'time_base' to value '1/44100'
FFMPEG: [Parsed_abuffer_1 @ 0ddfe500] Setting 'sample_rate' to value '44100'
FFMPEG: [Parsed_abuffer_1 @ 0ddfe500] Setting 'sample_fmt' to value '1'
FFMPEG: [Parsed_abuffer_1 @ 0ddfe500] Setting 'channel_layout' to value '3'
FFMPEG: [Parsed_abuffer_1 @ 0ddfe500] Setting 'channels' to value '2'
FFMPEG: [Parsed_abuffer_1 @ 0ddfe500] tb:1/44100 samplefmt:s16 samplerate:44100 chlayout:3
FFMPEG: [Parsed_volume_3 @ 0ddfe580] Setting 'volume' to value '2'
FFMPEG: [Parsed_aresample_4 @ 0ddfe660] Setting 'sample_rate' to value '48000'
FFMPEG: [Parsed_aformat_5 @ 0ddfe940] Setting 'sample_fmts' to value 'fltp'
FFMPEG: [Parsed_aformat_5 @ 0ddfe940] Setting 'channel_layouts' to value '3'

然后,我试图添加一个帧(已经预先解码过),如下所示:

// "buffer_source_context" is one of the "inputs[i].buffer_source_context" from the code above
int result = av_buffersrc_write_frame(  buffer_source_context,
                                            input_frame);
if (result < 0) {
    char errorBuf[1024];
    av_make_error_string(errorBuf, 1024, result);
    log("Error: While adding to buffer source: %s", errorBuf);
    return AVERROR_EXIT;
}

结果是提到的“无效论证”。

buffer_source_context是上面代码中提到的那些之一,input_frame也很好。 在添加过滤代码之前,相同的帧被传递给编码器而没有问题。

我对这里的错误感到茫然。我将FFmpeg错误记录在尽可能低的级别,但没有显示单个错误。我正在使用FFmpeg 3.1.1。

1 个答案:

答案 0 :(得分:1)

事实证明,问题是输入(解码器)的AVCodecContext的初始化。
正如您在我的问题中所看到的,缓冲滤波器的channel_layouts设置为3(这意味着立体声)。该值直接取自输入的AVCodecContext。

因此,当然,人们会假设从输入读取和解码的帧将在该通道布局中 出于某种原因,他们不是。相反,我必须在AVCodecContext上将channel_layoutrequested_channel_layout设置为立体声(AV_CH_LAYOUT_STEREO),然后再打开它。

最终让我得出的结论是吞下了查看FFmpeg源代码的苦药(在这种特定情况下不能进行实时调试)并找到可能为该函数抛出无效参数错误的地方。登记/> 我找到了很多类似的地方:

    int ch = src->channels;

    if (!ch) {
        ret = AVERROR(EINVAL);
        goto fail;
    }

所以我检查了所有候选人,最后发现通道布局不匹配。

如果FFmpeg的作者会花一些时间输出实际上有用的错误信息,那么我就不会在大海捞针中找到针头。

代码仍然不起作用,因为似乎一次有多个输入使av_read_frame几乎停止,但这与此问题无关。但是问题中的代码部分总是正确的(至少我认为是这样),错误发生在其他地方。