c++ - 如何正确释放 BitStreamFilter (bsf) 而不会出现双重释放错误?

标签 c++ memory ffmpeg

我正在尝试编写一个通过 ffmpeg 处理 MP4 电影的 C++ 类。

首先,我创建了几个与 std::unique<>() 一起使用的函数这样即使出现异常,事情也会被释放。

但是,当我尝试释放 BitStreamFilter 对象时,我得到了双重释放,但文档明确指出每个 av_bsf_alloc()必须与 av_bsf_free() 配对打电话。

@param ctx a pointer into which the pointer to the newly-allocated context will be written. It must be freed with av_bsf_free() after the filtering is done.

注意:强调我的。

但是,当时我调用 avformat_close_input()即使我没有使用这两个上下文做任何事情,我也会收到双重释放错误!?我认为可能有一个数据包既分配又尝试释放。但由于这两个上下文没有直接连接,我真的不明白它们最终如何两次释放某些东西。

下面是重现错误的代码(至少在 amd64 平台上)。编译完成后,直接执行即可。确保指定文件名,如下所示:

./ffmpeg_demuxer_test mymovie.mp4

我使用以下命令来编译和链接代码:

$ /usr/bin/c++ -std=c++17 -DDEBUG -D_DEBUG -D_GLIBCXX_ASSERTIONS -g -O0 \
    -fsanitize=address -fsanitize=enum -fsanitize=unreachable \
    -o ffmpeg_demuxer_test ffmpeg_demuxer_test.cpp \
    -lavformat -lavcodec

注意 -fsanitize=...用于捕获错误(例如双重释放错误)的选项。

这是代码:

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


void ffmpeg_demuxer_avformat_context_free(AVFormatContext * context)
{
    if(context != nullptr) avformat_close_input(&context);
}

void ffmpeg_demuxer_av_bsf_free(AVBSFContext * context)
{
    if(context != nullptr) av_bsf_free(&context);
}


int main(int argc, char * argv [])
{
    if(argc != 2)
    {
        std::cout << "Usage: " << argv[0] << " movie.mp4" << std::endl;
        exit(1);
    }

    // init the AV libraries
    //
    av_register_all();
    avformat_network_init();

    // allocate the AVFormatContext
    //
    AVFormatContext * format_context(nullptr);
    int const r1(avformat_open_input(
                  &format_context
                , argv[1]
                , nullptr           // input format
                , nullptr));        // options
    if(r1 != 0
    || format_context == nullptr)
    {
        throw std::bad_alloc();
    }

    auto f_format_context = std::unique_ptr<
              AVFormatContext
            , decltype(&ffmpeg_demuxer_avformat_context_free)>(
                      format_context
                    , &ffmpeg_demuxer_avformat_context_free);


    // now allocate a stream
    //
    if(avformat_find_stream_info(f_format_context.get(), nullptr) < 0)
    {
        throw std::runtime_error("ffmpeg: Could not find stream info");
    }

    auto f_video_stream_index = av_find_best_stream(
              f_format_context.get()
            , AVMEDIA_TYPE_VIDEO
            , -1            // wanted stream (any)
            , -1            // related stream (none)
            , nullptr       // AVCodec *
            , 0);           // flags
    if(f_video_stream_index < 0)
    {
        throw std::runtime_error("ffmpeg: Could not find stream in input file");
    }
    if(static_cast<unsigned int>(f_video_stream_index) >= f_format_context->nb_streams)
    {
        throw std::range_error("ffmpeg: Stream index out of range");
    }

    auto f_stream = f_format_context->streams[f_video_stream_index];

    auto f_video_codec = f_stream->codecpar->codec_id;

    int f_bit_depth(0);
    switch(f_stream->codecpar->format)
    {
    case AV_PIX_FMT_YUV420P10LE:
        f_bit_depth = 10;
        break;

    case AV_PIX_FMT_YUV420P12LE:
        f_bit_depth = 12;
        break;

    default:
        f_bit_depth = 8;
        break;

    }

    bool f_mp4_h264 = f_video_codec == AV_CODEC_ID_H264 && (
               strcmp(f_format_context->iformat->long_name, "QuickTime / MOV") == 0
            || strcmp(f_format_context->iformat->long_name, "FLV (Flash Video)") == 0
            || strcmp(f_format_context->iformat->long_name, "Matroska / WebM") == 0
        );

    if(f_mp4_h264)
    {
        AVBitStreamFilter const * bsf_stream_filter(av_bsf_get_by_name("h264_mp4toannexb"));
        if(bsf_stream_filter == nullptr)
        {
            throw std::runtime_error("av_bsf_get_by_name(\"h264_mp4toannexb\") failed");
        }
        AVBSFContext * bsf_context(nullptr);
        int const r2(av_bsf_alloc(bsf_stream_filter, &bsf_context));
        if(r2 < 0
        || bsf_context == nullptr)
        {
            throw std::bad_alloc();
        }
        auto f_bsf_context = std::unique_ptr<
                  AVBSFContext
                , decltype(&ffmpeg_demuxer_av_bsf_free)>(
                          bsf_context
                        , &ffmpeg_demuxer_av_bsf_free);
        f_bsf_context->par_in = f_stream->codecpar;
        if(av_bsf_init(f_bsf_context.get()) < 0)
        {
            throw std::runtime_error("av_bsf_init() failed");
        }
    }

    return 0;
}

所以。我是否误读了文档或误用了释放/关闭功能之一?我不认为AVBSFContext当我关闭 AVFormatContext 时,它本身就会被释放,但我可能弄错了?

我不会把“C”作为标签,因为即使这适用于 C,它也会被删除。这并不是因为我使用 C++,所以我得到了双重释放(即它发生在 ffmpeg C 库中) ).

最佳答案

它双重释放了您在此处设置的 AVCodecParameters*:

f_bsf_context->par_in = f_stream->codecpar;

您应该进行深层复制。 utils.c 中有一个 avcodec_parameters_copy

关于c++ - 如何正确释放 BitStreamFilter (bsf) 而不会出现双重释放错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65782600/

相关文章:

c++ - 什么可能导致 OpenGL 在 "Start Debugging"和 "Start without debugging"选项下表现不同?

java - 在Windows中使用Eclipse增加Java堆大小

javascript - 如何使用 1 个全局内存数组模拟 JavaScript 中带有参数和局部变量的调用堆栈?

c - 使用 memset 初始化三值结构数组

c++ - 整数自动转换为 double 而不是 float

c++ - 链接器脚本 - 在内存区域的末尾放置一个节

c++ - c/c++ Linux 允许的最大互斥量

linux - ffmpeg 将 .avi、.mp4、.mp3、.flv、.mkv 转换为 mp4

vb6 - 如何将ffmpeg转码过程信息链接到vb6 GUI应用程序中?

ffmpeg - 一次执行 2 个或更多命令