我正在尝试编写一个通过 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 withav_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/