调用avformat_find_stream_info导致无法解码简单的PNG图像?

3
我在使用libav解码一个简单的PNG图像时遇到了问题。调用avcodec_decode_video2后,decode_ok标志被设置为0,尽管数据包包含了整个图像。通过一些实验,我已经查明了问题所在,它似乎与调用avformat_find_stream_info有关。如果移除这个调用,示例将成功运行。然而,我希望能够在其他媒体上使用相同的代码,并且文档中建议调用avformat_find_stream_info
以下是说明此问题的最小化示例(不幸的是仍然有点冗长):
#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;
}
avformat_find_stream_info 前的格式转储打印如下:
Input #0, image2, from '/path/to/test.png':
  Duration: N/A, bitrate: N/A
    Stream #0:0, 0, 1/25: Video: png, 25 tbn
avformat_find_stream_info 后的格式转储打印如下:
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
因此看起来搜索结果可能提供有用的信息。有人可以解决这个问题吗?其他图像格式似乎工作正常。我认为这是一个简单的用户错误,而不是bug。
编辑:调试日志已经启用,但PNG解码器没有产生太多输出。我还尝试了设置自定义日志回调函数。
这是在解码成功时没有调用 avformat_find_stream_info 的情况下获得的输出:
Statistics: 52125 bytes read, 0 seeks
这是在调用 avformat_find_stream_info 且解码失败时所得到的输出:
Statistics: 52125 bytes read, 0 seeks
detected 8 logical cores
图片大小为52125字节,因此整个文件都被读取。我不确定逻辑核心是指什么。

1
代码看起来没问题。请遵循此Stack Overflow并使用vprintf(szFmt,varg)来让我们知道libav正在输出什么调试文本。 - Jack
@Jack 感谢您查看代码,特别是由于libav的性质而导致代码有些冗长。我已经添加了所请求的调试输出,但不幸的是它并不多(其他格式的解码器更加健谈)。 - kloffy
1个回答

3

在 libav 中似乎存在一些多线程问题。禁用多线程可以解决这个问题。

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

确实,那解决了问题。干得好!我在想是否禁用多线程是个好主意,以及它会如何影响性能。 - kloffy
如果您的目标是解码单独的图像,则不会有严重的性能影响。据我所知,FFmpeg并行化基本上是基于同时解码多个视频帧,而不是在多个线程中解码一个帧。 - Artem Markov
听起来很合理。我想在我的图像加载函数中禁用多线程是现在一个令人满意的解决方案。希望这个问题最终会得到解决。 - kloffy
Libav一直与线程有着奇怪的关系。大多数流/上下文的设置必须仔细保护,但我从未见过由于内部线程而导致编解码器失败的情况。很好的发现。 - Jack

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接