1

I am trying to use FFmpeg C api to create a filter to merge two audio streams. Trying to follow the code from here: Implementing a multiple input filter graph with the Libavfilter library in Android NDK

Everything seems to be alright.

However, as soon as I call av_buffersrc_write_frame (or av_buffersrc_add_frame or av_buffersrc_add_frame_flags, it doesn't matter), FFmpeg just reports "invalid argument" and nothing else - an utterly useless error message, as it could mean everything.
Which argument is invalid? What is wrong about it? Nobody knows.

I am initializing the graph and "grabbing" the contexts of the buffer sources for later use like this:

// 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;
    }
}

There are absolutely no errors in the initialization. At least FFmpeg has only this to say about it, which I think looks good:

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'

Then, I am trying to add a frame (that has been decoded beforehand) like this:

// "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;
}

And the result is the mentioned "invalid argument".

The buffer_source_context is one of those noted from the code above and the input_frame is perfectly fine as well. Before the filtering code was added, the same frame was passed to an encoder instead without a problem.

I am at a loss at what the error could be here. I log FFmpeg errors at the lowest possible level, but not a single error is shown. I am using FFmpeg 3.1.1.

Community
  • 1
  • 1
TheSHEEEP
  • 2,961
  • 2
  • 31
  • 57
  • A -1, a close vote, removal of a tag and no comment to any of it. Why, that is terribly useful, thank you ;) – TheSHEEEP Jan 30 '17 at 14:17
  • I'm not entirely sure by the looks of your code, but shouldn't it be the 'filtercontext' instead or the 'buffer_source_context' when writing the frame? – WLGfx Jan 30 '17 at 14:27
  • I'm not sure I understand. AVFilterContext* is the type of 'buffer_source_context'. And you have to add frames to the buffer source of a filter graph in order to have FFmpeg process them. And as I wrote, 'buffer_source_context' is one of the buffer source contexts from this line: 'inputs[i].buffer_source_context = filterContext;' – TheSHEEEP Jan 30 '17 at 14:30
  • Tried to make that a bit more clear now. – TheSHEEEP Jan 30 '17 at 14:37
  • 1
    I know you're working with audio frames and filters are quite complex when working with them in code. I've worked with the yadif filter for deinterlacing and it's far from what you're asking as I can't be specific for audio, but my working video filter: http://stackoverflow.com/a/40018558/2979092 If I knew more about the audio filter I'd be happy to help. – WLGfx Jan 30 '17 at 14:38
  • Yes, that is what I did before, too. But it only works for single inputs. As soon as you have multiple inputs (or outputs, I guess). You can no longer use "in" as the name for the input. And there is 0 documentation as for what names you'd have to use instead, or how to connect more than one input using the "classical" way. Hence I was looking at a way to have multiple inputs in code and the link in my question is the only (!!) thing I found. The FFmpeg examples again prove to be useless as they only display the very simplest case. – TheSHEEEP Jan 30 '17 at 15:45
  • 1
    Here here to very limited documentation. When I was using the yadif filter, my best source came from ffplay.c which helped a lot. So maybe https://ffmpeg.org/doxygen/3.0/ffmpeg__filter_8c_source.html may also help out. – WLGfx Jan 30 '17 at 16:03
  • 1
    The most likely scenario where this error happens is if the input AVFrame parameters (like samplerate, format, channels) do not match the configuration you set on the input `abuffer` you're working with. Can you print the relevant parameters from the input `AVFrame` you're pushing into the `abuffer` source? – Ronald S. Bultje Jan 31 '17 at 13:32

1 Answers1

2

As it turns out, the problem was the initialization of the AVCodecContext of the inputs (decoders).
As you can see in my question, the channel_layouts of the abuffer filters is set to 3 (which means stereo). This value was directly taken from the AVCodecContext of the inputs.

So, naturally, one would assume that frames read and decoded from the inputs would be in that channel layout.
For some reason, they were not. Instead, I had to set both channel_layout and requested_channel_layout on the AVCodecContext to stereo (AV_CH_LAYOUT_STEREO) before opening it.

What in the end brought me to that conclusion was swallowing the bitter pill of looking at the FFmpeg source code (live debugging not possible in this specific case) and finding the places that could throw an invalid argument error for that function.
I found many places similar to this:

    int ch = src->channels;

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

So I checked all of the candidates to finally figure out there was a mismatch in the channel layouts.

If the FFmpeg authors would commit some time to output actually helpful error messages with such cases, I wouldn't have lost almost two days looking for the needle in the haystack.

Code still doesn't work, because seemingly having more than one input at once grinds av_read_frame almost to a halt, but that is unrelated to this question. But the portion of code in the question was always correct (at least I assume that), the error was somewhere else.

TheSHEEEP
  • 2,961
  • 2
  • 31
  • 57