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

1  av_malloc() 


av_malloc() 声明:

  • 所属库:libavutil,该库是ffmpeg的功能库,提供了线程,内存,文件,加密等功能
  • 头文件:libavutil/mem.h
  • 该函数作用在于给对象分配内存块,并且是内存对齐的
  • 该函数由两个宏av_malloc_attrib  && av_alloc_size(1)指定了一些编译器属性,作用如后面所描述,更详细的描述见 FFMPEG4.1源码分析之 内存分配
/**
 * Allocate a memory block with alignment suitable for all memory accesses
 * (including vectors if available on the CPU).
 *
 * @param size Size in bytes for the memory block to be allocated
 * @return Pointer to the allocated block, or `NULL` if the block cannot
 *         be allocated
 * @see av_mallocz()
 */
void *av_malloc(size_t size) av_malloc_attrib av_alloc_size(1);
  1. av_malloc_attrib是一个宏定义,如果是在编译器GCC3.1及以上版本的情况下,给方法av_malloc增加属性 __attribute__((__malloc__)),该属性指示编译器按照malloc函数来对待本函数,并且可以对其实施相应的优化措施。后续在研究ffmpeg中内存分配与管理的源码之后,会再尝试写一篇文章详细介绍。此时,先给出几个网上关于该属性的描述相关网址:http://www.keil.com/support/man/docs/armcc/armcc_chr1359124975555.htm;  https://stackoverflow.com/questions/18485447/gcc-attribute-malloc;  https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-g_t_0040code_007bmalloc_007d-function-attribute-3251
    /**
     * @def av_malloc_attrib
     * Function attribute denoting a malloc-like function.
     *
     * @see <a href="https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-g_t_0040code_007bmalloc_007d-function-attribute-3251">Function attribute `malloc` in GCC's documentation</a>
     */
    
    #if AV_GCC_VERSION_AT_LEAST(3,1)
        #define av_malloc_attrib __attribute__((__malloc__))
    #else
        #define av_malloc_attrib
    #endif
  2. av_alloc_size(1)也是一个宏定义,如果是在编译器GCC4.3及以上版本的情况下,给方法增加一个属性__attribute__((alloc_size(1))),告知编译器av_malloc(size_t size)方法的第一个参数,也即size是要分配的空间大小,关于__attribute__((alloc_size(__VA_ARGS__)))属性的详细描述可以见:https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-g_t_0040code_007balloc_005fsize_007d-function-attribute-3220

 av_malloc() 源码:

void *av_malloc(size_t size)
{
    void *ptr = NULL;

    /* let's disallow possibly ambiguous cases */
    if (size > (max_alloc_size - 32))
        return NULL;

#if HAVE_POSIX_MEMALIGN
    if (size) //OS X on SDK 10.6 has a broken posix_memalign implementation
    if (posix_memalign(&ptr, ALIGN, size))
        ptr = NULL;
#elif HAVE_ALIGNED_MALLOC
    ptr = _aligned_malloc(size, ALIGN);
#elif HAVE_MEMALIGN
#ifndef __DJGPP__
    ptr = memalign(ALIGN, size);
#else
    ptr = memalign(size, ALIGN);
#endif
    /* Why 64?
     * Indeed, we should align it:
     *   on  4 for 386
     *   on 16 for 486
     *   on 32 for 586, PPro - K6-III
     *   on 64 for K7 (maybe for P3 too).
     * Because L1 and L2 caches are aligned on those values.
     * But I don't want to code such logic here!
     */
    /* Why 32?
     * For AVX ASM. SSE / NEON needs only 16.
     * Why not larger? Because I did not see a difference in benchmarks ...
     */
    /* benchmarks with P3
     * memalign(64) + 1          3071, 3051, 3032
     * memalign(64) + 2          3051, 3032, 3041
     * memalign(64) + 4          2911, 2896, 2915
     * memalign(64) + 8          2545, 2554, 2550
     * memalign(64) + 16         2543, 2572, 2563
     * memalign(64) + 32         2546, 2545, 2571
     * memalign(64) + 64         2570, 2533, 2558
     *
     * BTW, malloc seems to do 8-byte alignment by default here.
     */
#else
    ptr = malloc(size);
#endif
    if(!ptr && !size) {           // 在size为0时,将分配一个字节的空间
        size = 1;
        ptr= av_malloc(1);
    }
#if CONFIG_MEMORY_POISONING
    if (ptr)
        memset(ptr, FF_MEMORY_POISON, size);
#endif
    return ptr;
}
  1. 该函数在不同的宏作用下使用了不同的内存分配函数,优先级为posix_memalign() ->_aligned_malloc() -> memalign() -> malloc()。这些宏定义在文件config.h中,这个文件不属于ffpmeg的任何库,这个是ffmpeg源码编译时,configure命令生成的配置文件,在不同的环境下,config.h文件中的宏定义值是不一样的。
  2. 就我当前的编译环境Windows系统,VisualStudio2013update5来说,这几个宏定义分别为如下源代码所示,因此将会使用_aligned_malloc()方法进行内存分配,而该方法定义在如下平台相关的文件中,见下图,另外该方法的详细描述见:MicroSoft Docs:_aligned_malloc
    #define HAVE_POSIX_MEMALIGN 0
    #define HAVE_ALIGNED_MALLOC 1
    #define HAVE_MEMALIGN 0
    __DJGPP__ 未定义
  3. 内存对齐:上述几个函数除了malloc外都具有内存对齐功能,另外还有一个宏ALIGN ,其根据另外两个宏HAVE_AVX512 和HAVE_AVX来决定是按多少位对齐,而HAVE_AVX和HAVE_AVX512指示环境中是否可以使用AVX CPU指令集和AVX512 CPU扩展指令集,能使用更高级的指令集意味着可以使用的寄存器大小会变大,因此对齐的数据位数也会变大。关于AVX与AVX512的详细描述见wikipedia:AVX指令集AVX512

    #define ALIGN (HAVE_AVX512 ? 64 : (HAVE_AVX ? 32 : 16))
  4. 注意到当size=0时,将分配一个字节的空间,这儿是令我奇怪的一点,为什么此处不直接返回NULL就行了?突然又想到在C/C++的空结构体/空类都会占据一个字节以作占位符,见如下示例代码以及结果展示。猜测这儿应该也是起同样的作用。

    #include <stdio.h>
    
    struct EmptyStruct{};
    class EmptyClass{};
    
    int main(int argc, char* argv[]) 
    {
        printf("EmptyStruct'size is %d\n", sizeof(EmptyStruct));
        printf("EmptyClass'size is %d\n", sizeof(EmptyClass));
        getchar();
        return 0;
    }

  5. 最后,宏CONFIG_MEMORY_POISONING决定了分配后的数据是否需要使用数据填充,根据该宏的名称也知道是“毒化,中毒”的意思,通常我们分配内存后一般都是使用0去初始化这块儿内存,但是ffmpeg中如果配置了上述宏定义,那么会使用FF_MEMORY_POISON指定的0x2a去填充分配空间的每个字节。当然,就我目前的环境而言,config.h文件中的 CONFIG_MEMORY_POISONING 被定义为0,也即不进行内存“毒化”。

    #define CONFIG_MEMORY_POISONING 0
    #define FF_MEMORY_POISON 0x2a

2 av_mallocz()


av_mallocz() 声明:

void *av_mallocz(size_t size) av_malloc_attrib av_alloc_size(1);

av_mallocz()  源码: 

void *av_mallocz(size_t size)
{
    void *ptr = av_malloc(size); 使用av_malloc分配内存
    if (ptr)
        memset(ptr, 0, size);    将分配的内存块所有字节置0
    return ptr;
}
以下是一个简单的示例代码,可以将 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,如果您需要使用其他像素格式,也需要相应地修改代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值