FFMPEG4.1源码分析之 内存管理APIs av_freep() && av_free()

1. av_freep()


av_freep() 声明:

  • 所属库:libavutil(lavu),libavutil是ffmpeg的工具类库,本函数是其内存管理类库中的函数
  • 头文件:libavutil/mem.h
  • 声明:释放一个内存块,这个内存块由av_malloc()/av_realloc()家族函数来分配的,并且将指针置为NULL。注意看函数说明,对于av_free()函数的使用会导致悬空指针,而av_freep()函数不会导致悬空指针,关于悬空指针的论述见文章 野(wild)指针与悬空(dangling)指针
               1) 重点一:该函数可以解决悬空指针的问题!!!这个确实是很大的改进!!!,如何实现的见后文分析
               2) 重点二:传入的ptr解释为“Pointer to the pointer to the memory block which should be freed”,ptr应该是双重指针!!!
  • /**
     * Free a memory block which has been allocated with a function of av_malloc()
     * or av_realloc() family, and set the pointer pointing to it to `NULL`.
     *
     * @code{.c}
     * uint8_t *buf = av_malloc(16);   
     * av_free(buf);
     * // buf now contains a dangling pointer to freed memory, and accidental
     * // dereference of buf will result in a use-after-free, which may be a
     * // security risk.
     *
     * uint8_t *buf = av_malloc(16);
     * av_freep(&buf);
     * // buf is now NULL, and accidental dereference will only result in a
     * // NULL-pointer dereference.
     * @endcode
     *
     * @param ptr Pointer to the pointer to the memory block which should be freed
     * @note `*ptr = NULL` is safe and leads to no action.
     * @see av_free()
     */
    void av_freep(void *ptr);

av_freep() 源码:

  • 源文件:libavutil/mem.c
    void av_freep(void *arg)
    {
        void *val;
    
        memcpy(&val, arg, sizeof(val));
        memcpy(arg, &(void *){ NULL }, sizeof(val));
        av_free(val);
    }
    
     * uint8_t *buf = av_malloc(16);
     * av_freep(&buf);
  1. 虽然av_freep()函数很简单,就几行代码,但实际上很容易让人晕头,以一个示例来讲会比较清楚明白,如上述两行代码所示,展示了av_freep()函数的一般用法,注意,我们需要释放的是buf指向的内存,而传入av_freep()的并非是指针buf,而是buf指针所在的地址,又是一个double point。哎,一到双重指针就犯晕。
  2. memcpy(&val, arg, sizeof(val)); 该函数将arg指向的内存地址开始的数据,拷贝sizeof(val)个byte数据,到&val开头的地址处。我们一个个来分析都是啥:
            1)如示例所知,arg为&buf,那么arg指向的buf这个指针;
            2)由于val类型为void*,因此,sizeof(val)为地址长度,一般是4字节;   
            3)由1)2)可知其实就是拷贝buf的值,也即需要释放的空间(由av_malloc(16)分配的空间)的地址
            4)这个地址值拷贝到&val,也即让val这个指针指向这个空间咯,务必理解这点。
  3.  memcpy(arg, &(void *){ NULL }, sizeof(val)); 有了2做分析基础,那么可以知道就是往arg指向的地址赋值为NULL,arg指向的地址是什么呢?arg为&buf,其实就是让buf这个指针指向了NULL,即buf=NULL。这样就解决了悬空指针的问题
  4.  av_free(val)来释放av_malloc(16)分配的空间,见后续av_free()的分析。

2 av_free()


av_free() 声明:

  • 所属库:libavutil(lavu) ,libavutil是ffmpeg的工具类库,本函数是其内存管理类库中的函数
  • 头文件:libavutil/mem.h
  • 声明:释放一个使用av_malloc()/av_realloc()函数分配的内存块。
              1)ptr就于av_freep()的入参ptr不一样咯,“Pointer to the memory block which should be freed”,直接就是指向要释放空间的指针了;
              2)ptr可以为NUL
              3)推荐使用av_freep()而非av_free(),以避免留下悬空指针~~
    /**
     * Free a memory block which has been allocated with a function of av_malloc()
     * or av_realloc() family.
     *
     * @param ptr Pointer to the memory block which should be freed.
     *
     * @note `ptr = NULL` is explicitly allowed.
     * @note It is recommended that you use av_freep() instead, to prevent leaving
     *       behind dangling pointers.
     * @see av_freep()
     */
    void av_free(void *ptr);
    

av_free() 源码:

  • 源文件:libavutil/mem.c
    void av_free(void *ptr)
    {
    #if HAVE_ALIGNED_MALLOC
        _aligned_free(ptr);
    #else
        free(ptr);
    #endif
    }
  1.  注意HAVE_ALIGNED_MALLOC存在的情况下,ffmpeg使用av_malloc分配内存时,使用_aligned_malloc()库函数,因此,此处对应的也是使用内存对齐的释放函数_aligned_free()
    #elif HAVE_ALIGNED_MALLOC
        ptr = _aligned_malloc(size, ALIGN);
    #elif HAVE_MEMALIGN
  2. 否则使用常规的c运行库 free()函数进行内存释放。
以下是一个简单的示例代码,可以将 OpenCV 的 `cv::Mat` 对象转换为 FFmpeg 的 `AVFrame`,并将其编码为 YUV 420P 格式。 ```c++ #include <opencv2/opencv.hpp> #include <libavformat/avformat.h> #include <libavcodec/avcodec.h> #include <libswscale/swscale.h> int main() { // 初始化 FFmpeg av_register_all(); // 创建格式上下文 AVFormatContext* format_ctx = avformat_alloc_context(); if (!format_ctx) { std::cerr << "Failed to allocate format context" << std::endl; return -1; } // 设置输出格式 AVOutputFormat* output_fmt = av_guess_format("mp4", nullptr, nullptr); if (!output_fmt) { std::cerr << "Failed to guess output format" << std::endl; return -1; } format_ctx->oformat = output_fmt; // 打开输出文件 AVIOContext* io_ctx = nullptr; if (avio_open(&io_ctx, "output.mp4", AVIO_FLAG_WRITE) < 0) { std::cerr << "Failed to open output file" << std::endl; return -1; } format_ctx->pb = io_ctx; // 创建视频流 AVStream* video_stream = avformat_new_stream(format_ctx, nullptr); if (!video_stream) { std::cerr << "Failed to create video stream" << std::endl; return -1; } // 设置编码器参数 AVCodecParameters* codec_params = video_stream->codecpar; codec_params->codec_type = AVMEDIA_TYPE_VIDEO; codec_params->codec_id = output_fmt->video_codec; codec_params->width = 640; codec_params->height = 480; codec_params->format = AV_PIX_FMT_YUV420P; // 查找编码器 AVCodec* codec = avcodec_find_encoder(output_fmt->video_codec); if (!codec) { std::cerr << "Failed to find encoder" << std::endl; return -1; } // 创建编码器上下文 AVCodecContext* codec_ctx = avcodec_alloc_context3(codec); if (!codec_ctx) { std::cerr << "Failed to allocate codec context" << std::endl; return -1; } codec_ctx->codec_type = AVMEDIA_TYPE_VIDEO; codec_ctx->width = codec_params->width; codec_ctx->height = codec_params->height; codec_ctx->pix_fmt = codec_params->format; codec_ctx->time_base = {1, 25}; // 打开编码器 if (avcodec_open2(codec_ctx, codec, nullptr) < 0) { std::cerr << "Failed to open codec" << std::endl; return -1; } // 创建帧 AVFrame* frame = av_frame_alloc(); if (!frame) { std::cerr << "Failed to allocate frame" << std::endl; return -1; } frame->format = codec_ctx->pix_fmt; frame->width = codec_ctx->width; frame->height = codec_ctx->height; // 分配帧数据空间 if (av_frame_get_buffer(frame, 0) < 0) { std::cerr << "Failed to allocate frame data" << std::endl; return -1; } // 创建格式转换器 SwsContext* sws_ctx = sws_getContext(codec_ctx->width, codec_ctx->height, AV_PIX_FMT_BGR24, codec_ctx->width, codec_ctx->height, codec_ctx->pix_fmt, SWS_BICUBIC, nullptr, nullptr, nullptr); if (!sws_ctx) { std::cerr << "Failed to create format converter" << std::endl; return -1; } // 读取输入帧 cv::Mat input_frame = cv::imread("input.jpg"); if (input_frame.empty()) { std::cerr << "Failed to read input frame" << std::endl; return -1; } // 转换输入帧 uint8_t* input_data[AV_NUM_DATA_POINTERS] = {0}; input_data[0] = input_frame.data; int input_linesize[AV_NUM_DATA_POINTERS] = {0}; input_linesize[0] = input_frame.step; sws_scale(sws_ctx, input_data, input_linesize, 0, codec_ctx->height, frame->data, frame->linesize); // 编码帧 AVPacket pkt; av_init_packet(&pkt); pkt.data = nullptr; pkt.size = 0; int got_packet = 0; if (avcodec_encode_video2(codec_ctx, &pkt, frame, &got_packet) < 0) { std::cerr << "Failed to encode frame" << std::endl; return -1; } // 写入输出文件 if (got_packet) { av_packet_rescale_ts(&pkt, codec_ctx->time_base, video_stream->time_base); pkt.stream_index = video_stream->index; if (av_interleaved_write_frame(format_ctx, &pkt) < 0) { std::cerr << "Failed to write packet" << std::endl; return -1; } av_packet_unref(&pkt); } // 写入文件尾 av_write_trailer(format_ctx); // 释放资源 avcodec_free_context(&codec_ctx); av_frame_free(&frame); avio_closep(&format_ctx->pb); avformat_free_context(format_ctx); sws_freeContext(sws_ctx); return 0; } ``` 需要注意的是,上述代码中的 `AV_PIX_FMT_BGR24` 表示输入图像的像素格式,如果您的输入图像格式不是 BGR24,需要相应地修改代码。另外,上述代码中的像素格式硬编码为 YUV420P,如果您需要使用其他像素格式,也需要相应地修改代码。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值