Question

I am running into a problem decoding a simple PNG image with libav. The decode_ok flag after the call to avcodec_decode_video2 is set to 0, even though the packet contains the entire image. Through some experimentation, I have managed to pinpoint the issue and it seems related to calling avformat_find_stream_info. If the call is removed, the example runs successfully. However, I would like to use the same code for other media, and calling avformat_find_stream_info is recommended in the documentation.

The following minimal example illustrates the behavior (unfortunately still a bit lengthy):

#include <iostream>

extern "C"
{
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
}

// Nothing to see here, it's just a helper function
AVCodecContext* open(AVMediaType mediaType, AVFormatContext* formatContext)
{
    auto ret = 0;
    if ((ret = av_find_best_stream(formatContext, mediaType, -1, -1, nullptr, 0)) < 0)
    {
        std::cerr << "Failed to find video stream." << std::endl;
        return nullptr;
    }

    auto codecContext = formatContext->streams[ret]->codec;
    auto codec = avcodec_find_decoder(codecContext->codec_id);
    if (!codec)
    {
        std::cerr << "Failed to find codec." << std::endl;
        return nullptr;
    }

    if ((ret = avcodec_open2(codecContext, codec, nullptr)) != 0)
    {
        std::cerr << "Failed to open codec context." << std::endl;
        return nullptr;
    }

    return codecContext;
}

// All the interesting bits are here
int main(int argc, char* argv[])
{
    auto path = "/path/to/test.png"; // Replace with valid path to PNG
    auto ret = 0;

    av_log_set_level(AV_LOG_DEBUG);

    av_register_all();
    avcodec_register_all();

    auto formatContext = avformat_alloc_context();
    if ((ret = avformat_open_input(&formatContext, path, NULL, NULL)) != 0)
    {
        std::cerr << "Failed to open input." << std::endl;
        return -1;
    }
    av_dump_format(formatContext, 0, path, 0);

//*/ Info is successfully found, but interferes with decoding
    if((ret = avformat_find_stream_info(formatContext, nullptr)) < 0)
    {
        std::cerr << "Failed to find stream info." << std::endl;
        return -1;
    }
    av_dump_format(formatContext, 0, path, 0);
//*/

    auto codecContext = open(AVMEDIA_TYPE_VIDEO, formatContext);

    AVPacket packet;
    av_init_packet(&packet);

    if ((ret = av_read_frame(formatContext, &packet)) < 0)
    {
        std::cerr << "Failed to read frame." << std::endl;
        return -1;
    }

    auto frame = av_frame_alloc();
    auto decode_ok = 0;
    if ((ret = avcodec_decode_video2(codecContext, frame, &decode_ok, &packet)) < 0 || !decode_ok)
    {
        std::cerr << "Failed to decode frame." << std::endl;
        return -1;
    }

    av_frame_free(&frame);
    av_free_packet(&packet);

    avcodec_close(codecContext);

    avformat_close_input(&formatContext);
    av_free(formatContext);

    return 0;
}

The format dump before avformat_find_stream_info prints:

Input #0, image2, from '/path/to/test.png':
  Duration: N/A, bitrate: N/A
    Stream #0:0, 0, 1/25: Video: png, 25 tbn

The format dump after avformat_find_stream_info prints:

Input #0, image2, from '/path/to/test.png':
  Duration: 00:00:00.04, start: 0.000000, bitrate: N/A
    Stream #0:0, 1, 1/25: Video: png, rgba, 512x512 [SAR 3780:3780 DAR 1:1], 1/25, 25 tbr, 25 tbn, 25 tbc

So it looks like the search yields potentially useful information. Can anybody shed some light on this problem? Other image formats seem to work fine. I assume that this is a simple user error rather than a bug.

Edit: Debug logging was already enabled, but the PNG decoder does not produce a lot of output. I have also tried setting a custom logging callback.

Here is what I get without the call to avformat_find_stream_info, when decoding succeeds:

Statistics: 52125 bytes read, 0 seeks

And here is what I get with the call to avformat_find_stream_info, when decoding fails:

Statistics: 52125 bytes read, 0 seeks

detected 8 logical cores

The image is 52125 bytes, so the whole file is read. I am not sure what the logical cores are referring to.

Was it helpful?

Solution

Seems to be some multithreading issue in libav. Disabling multithreading fixes the problem.

codecContext->thread_count=1;
if ((ret = avcodec_open2(codecContext, codec, nullptr)) < 0)
{
    std::cerr << "Failed to open codec context." << std::endl;
    return nullptr;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top