1. 程式人生 > >FFmpeg原始碼簡單分析:記憶體的分配和釋放(av_malloc()、av_free()等)

FFmpeg原始碼簡單分析:記憶體的分配和釋放(av_malloc()、av_free()等)

=====================================================

FFmpeg的庫函式原始碼分析文章列表:

【架構圖】

【通用】

【解碼】

【編碼】

【其它】

【指令碼】

【H.264】

=====================================================


本文簡單記錄一下FFmpeg中記憶體操作的函式。

記憶體操作的常見函式位於libavutil\mem.c中。本文記錄FFmpeg開發中最常使用的幾個函式:av_malloc(),av_realloc(),av_mallocz(),av_calloc(),av_free(),av_freep()。

av_malloc()

av_malloc()是FFmpeg中最常見的記憶體分配函式。它的定義如下。

#define FF_MEMORY_POISON 0x2a

#define ALIGN (HAVE_AVX ? 32 : 16)

static size_t max_alloc_size= INT_MAX;

void *av_malloc(size_t size)
{
    void *ptr = NULL;
#if CONFIG_MEMALIGN_HACK
    long diff;
#endif


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


#if CONFIG_MEMALIGN_HACK
    ptr = malloc(size + ALIGN);
    if (!ptr)
        return ptr;
    diff              = ((~(long)ptr)&(ALIGN - 1)) + 1;
    ptr               = (char *)ptr + diff;
    ((char *)ptr)[-1] = diff;
#elif 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 = 1;
        ptr= av_malloc(1);
    }
#if CONFIG_MEMORY_POISONING
    if (ptr)
        memset(ptr, FF_MEMORY_POISON, size);
#endif
    return ptr;
}


如果不考慮上述程式碼中的一大堆巨集定義(即類似CONFIG_MEMALIGN_HACK這類的巨集都採用預設值0),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;
    ptr = malloc(size);
    if(!ptr && !size) {
        size = 1;
        ptr= av_malloc(1);
    }
    return ptr;
}

可以看出,此時的av_malloc()就是簡單的封裝了系統函式malloc(),並做了一些錯誤檢查工作。

關於size_t

size _t 這個型別在FFmpeg中多次出現,簡單解釋一下其作用。size _t是為了增強程式的可移植性而定義的。不同系統上,定義size_t可能不一樣。它實際上就是unsigned int。

為什麼要記憶體對齊?

FFmpeg記憶體分配方面多次涉及到“記憶體對齊”(memory alignment)的概念。
這方面內容在IBM的網站上有一篇文章,講的挺通俗易懂的,在此簡單轉述一下。

程式設計師通常認為記憶體就是一個位元組陣列,每次可以一個一個位元組存取記憶體。例如在C語言中使用char *指代“一塊記憶體”,Java中使用byte[]指代一塊記憶體。如下所示。


但那實際上計算機處理器卻不是這樣認為的。處理器相對比較“懶惰”,它會以2位元組,4位元組,8位元組,16位元組甚至32位元組來存取記憶體。例如下圖顯示了以4位元組為單位讀寫記憶體的處理器“看待”上述記憶體的方式。


上述的存取單位的大小稱之為記憶體存取粒度。
下面看一個例項,分別從地址0,和地址1讀取4個位元組到暫存器。

從程式設計師的角度來看,讀取方式如下圖所示。


而2位元組存取粒度的處理器的讀取方式如下圖所示。


可以看出2位元組存取粒度的處理器從地址0讀取4個位元組一共讀取2次;從地址1讀取4個位元組一共讀取了3次。由於每次讀取的開銷是固定的,因此從地址1讀取4位元組的效率有所下降。

4位元組存取粒度的處理器的讀取方式如下圖所示。


可以看出4位元組存取粒度的處理器從地址0讀取4個位元組一共讀取1次;從地址1讀取4個位元組一共讀取了2次。從地址1讀取的開銷比從地址0讀取多了一倍。由此可見記憶體不對齊對CPU的效能是有影響的。


av_realloc()

av_realloc()用於對申請的記憶體的大小進行調整。它的定義如下。

void *av_realloc(void *ptr, size_t size)
{
#if CONFIG_MEMALIGN_HACK
    int diff;
#endif


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


#if CONFIG_MEMALIGN_HACK
    //FIXME this isn't aligned correctly, though it probably isn't needed
    if (!ptr)
        return av_malloc(size);
    diff = ((char *)ptr)[-1];
    av_assert0(diff>0 && diff<=ALIGN);
    ptr = realloc((char *)ptr - diff, size + diff);
    if (ptr)
        ptr = (char *)ptr + diff;
    return ptr;
#elif HAVE_ALIGNED_MALLOC
    return _aligned_realloc(ptr, size + !size, ALIGN);
#else
    return realloc(ptr, size + !size);
#endif
}

預設情況下(CONFIG_MEMALIGN_HACK這些巨集使用預設值0)的程式碼:
void *av_realloc(void *ptr, size_t size)
{
    /* let's disallow possibly ambiguous cases */
    if (size > (max_alloc_size - 32))
        return NULL;
    return realloc(ptr, size + !size);
}

可以看出av_realloc()簡單封裝了系統的realloc()函式。


av_mallocz()

av_mallocz()可以理解為av_malloc()+zeromemory。程式碼如下。
void *av_mallocz(size_t size)
{
    void *ptr = av_malloc(size);
    if (ptr)
        memset(ptr, 0, size);
    return ptr;
}

從原始碼可以看出av_mallocz()中呼叫了av_malloc()之後,又呼叫memset()將分配的記憶體設定為0。

av_calloc()

av_calloc()則是簡單封裝了av_mallocz(),定義如下所示。
void *av_calloc(size_t nmemb, size_t size)
{
    if (size <= 0 || nmemb >= INT_MAX / size)
        return NULL;
    return av_mallocz(nmemb * size);
}

從程式碼中可以看出,它呼叫av_mallocz()分配了nmemb*size個位元組的記憶體。


av_free()

av_free()用於釋放申請的記憶體。它的定義如下。

void av_free(void *ptr)
{
#if CONFIG_MEMALIGN_HACK
    if (ptr) {
        int v= ((char *)ptr)[-1];
        av_assert0(v>0 && v<=ALIGN);
        free((char *)ptr - v);
    }
#elif HAVE_ALIGNED_MALLOC
    _aligned_free(ptr);
#else
    free(ptr);
#endif
}

預設情況下(CONFIG_MEMALIGN_HACK這些巨集使用預設值0)的程式碼:
void av_free(void *ptr)
{
    free(ptr);
}
可以看出av_free()簡單的封裝了free()。

av_freep()

av_freep()簡單封裝了av_free()。並且在釋放記憶體之後將目標指標設定為NULL。

void av_freep(void *arg)
{
    void **ptr = (void **)arg;
    av_free(*ptr);
    *ptr = NULL;
}

雷霄驊 (Lei Xiaohua)
[email protected]
http://blog.csdn.net/leixiaohua1020

相關推薦

FFmpeg原始碼簡單分析記憶體分配釋放av_malloc()av_free()

=====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】================================

FFmpeg原始碼簡單分析常見結構體的初始化銷燬AVFormatContext,AVFrame

=====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】================================

ffmpeg 原始碼簡單分析 av_read_frame()

此前寫了好幾篇ffmpeg原始碼分析文章,列表如下: ============================ ffmpeg中的av_read_frame()的作用是讀取碼流中的音訊若干幀或者視訊一幀。例如,解碼視訊的時

ffmpeg 原始碼簡單分析 av_register_all()

此前寫了好幾篇ffmpeg原始碼分析文章,列表如下: ============================ 前一陣子看了一下ffmpeg的原始碼,並且做了一些註釋,在此貼出來以作備忘。 本文分析一下ffmpeg註冊

FFmpeg原始碼簡單分析makefile

=====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】================================

FFmpeg原始碼簡單分析結構體成員管理系統-AVClass

=====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】================================

FFmpeg原始碼簡單分析avcodec_open2()

=====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】================================

FFmpeg原始碼簡單分析avformat_close_input()

=====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】================================

FFmpeg原始碼簡單分析av_write_frame()

=====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】================================

FFmpeg原始碼簡單分析avcodec_encode_video()

=====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】================================

FFmpeg原始碼簡單分析configure

=====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】================================

FFmpeg的H.264解碼器原始碼簡單分析概述

=====================================================H.264原始碼分析文章列表:【編碼 - x264】【解碼 - libavcodec H.264 解碼器】================================

FFmpeg的HEVC解碼器原始碼簡單分析CTU解碼CTU Decode部分-TU

=====================================================HEVC原始碼分析文章列表:【解碼 -libavcodec HEVC 解碼器】==============================================

FFmpeg的H.264解碼器原始碼簡單分析解碼器主幹部分

=====================================================H.264原始碼分析文章列表:【編碼 - x264】【解碼 - libavcodec H.264 解碼器】================================

FFmpeg的H.264解碼器原始碼簡單分析熵解碼Entropy Decoding部分

=====================================================H.264原始碼分析文章列表:【編碼 - x264】【解碼 - libavcodec H.264 解碼器】================================

H264編碼器5( x264原始碼簡單分析x264_slice_write() 與H264 編碼簡介)

  x264原始碼簡單分析:x264_slice_write() 來自:https://blog.csdn.net/leixiaohua1020/article/details/45536607     H264 編碼簡介 https://blo

H264編碼器4( x264原始碼簡單分析概述)

來自:https://blog.csdn.net/leixiaohua1020/article/details/45536607   ===================================================== H.264原始碼分析文章列表:

FFmpeg原始碼簡單分析 avio open2

                =====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】=================================

FFmpeg原始碼簡單分析 avcodec encode video

                =====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】=================================

Linux中的記憶體分配釋放之slab分配器分析

        我們在上篇文章分析cache_grow()函式的時候涉及兩個函式,我們沒有細說。一個就是kmem_getpages()和kmem_freepages()函式,這兩個函式有3個引數。kmem_cahce_t:主要是把申請到的物件加到這個快取記憶體內   flag