FFmpeg source code analysis: memory management system

FFmpeg has a dedicated memory management system, including: memory allocation, memory copying, and memory release. Memory allocation includes memory allocation and alignment, memory allocation and zeroing, allocation of memory blocks of specified size, reallocation of memory blocks, fast allocation of memory, allocation of memory of specified maximum value, allocation of array memory, fast allocation of array memory, and reallocation of arrays RAM.

The memory management of FFmpeg is located in libavutil/mem.c, and the related functions are shown in the following figure:


​​​​​​​

 

1. Memory allocation

1 、 av_malloc

av_malloc() memory allocation, and memory alignment, which is convenient for the system to quickly access memory. code show as below:

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

    if (size > max_alloc_size)
        return NULL;

#if HAVE_POSIX_MEMALIGN
    if (size)
    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;
}

2 、 av_mallocz

av_mallocz() is based on av_malloc() and calls memset() to clear the memory:

void *av_mallocz(size_t size)
{
    void *ptr = av_malloc(size);
    if (ptr)
        memset(ptr, 0, size);
    return ptr;
}

3 、 av_malloc_array

av_malloc_array() first calculates the size of the memory block required by the array, and then uses av_malloc() to allocate the array memory:

void *av_malloc_array(size_t nmemb, size_t size)
{
    size_t result;
    if (av_size_mult(nmemb, size, &result) < 0)
        return NULL;
    return av_malloc(result);
}

4 、 av_mallocz_array

av_mallocz_array() first calculates the memory block size required by the array, and then uses av_mallocz() to allocate the array memory:

void *av_mallocz_array(size_t nmemb, size_t size)
{
    size_t result;
    if (av_size_mult(nmemb, size, &result) < 0)
        return NULL;
    return av_mallocz(result);
}

5 、 av_calloc

av_calloc() operation and av_mallocz_array(), first calculate the memory size and then use av_mallocz() to allocate memory:

void *av_calloc(size_t nmemb, size_t size)
{
    size_t result;
    if (av_size_mult(nmemb, size, &result) < 0)
        return NULL;
    return av_mallocz(result);
}

6 、 av_max_alloc

av_max_alloc() mainly specifies the maximum value of allocated memory:

static size_t max_alloc_size= INT_MAX;

void av_max_alloc(size_t max)
{
    max_alloc_size = max;
}

In av_malloc(), it is used to determine whether the size exceeds the maximum value:

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

    if (size > max_alloc_size)
        return NULL;

    ......
}

7 、 av_realloc

av_realloc() encapsulates the system's realloc function and reallocates memory blocks:

void *av_realloc(void *ptr, size_t size)
{
    if (size > max_alloc_size)
        return NULL;

#if HAVE_ALIGNED_MALLOC
    return _aligned_realloc(ptr, size + !size, ALIGN);
#else
    return realloc(ptr, size + !size);
#endif
}

8 、 av_realloc_array

av_realloc_array() first calculates the memory block size, and then uses av_realloc() to reallocate the array memory:

void *av_realloc_array(void *ptr, size_t nmemb, size_t size)
{
    size_t result;
    if (av_size_mult(nmemb, size, &result) < 0)
        return NULL;
    return av_realloc(ptr, result);
}

9 、 by_fast_realloc

av_fast_realloc() quickly reallocates memory, if the original memory block is large enough to be reused directly:

void *av_fast_realloc(void *ptr, unsigned int *size, size_t min_size)
{
    if (min_size <= *size)
        return ptr;

    if (min_size > max_alloc_size) {
        *size = 0;
        return NULL;
    }

    min_size = FFMIN(max_alloc_size, FFMAX(min_size + min_size / 16 + 32, min_size));

    ptr = av_realloc(ptr, min_size);
    /* we could set this to the unmodified min_size but this is safer
     * if the user lost the ptr and uses NULL now
     */
    if (!ptr)
        min_size = 0;

    *size = min_size;

    return ptr;
}

10 、 av_fast_malloc

av_fast_malloc() allocates memory fast:

void av_fast_malloc(void *ptr, unsigned int *size, size_t min_size)
{
    ff_fast_malloc(ptr, size, min_size, 0);
}

The ff_fast_malloc() code is located in libavutil/mem_internal.h:

static inline int ff_fast_malloc(void *ptr, unsigned int *size, size_t min_size, int zero_realloc)
{
    void *val;

    memcpy(&val, ptr, sizeof(val));
    if (min_size <= *size) {
        av_assert0(val || !min_size);
        return 0;
    }
    min_size = FFMAX(min_size + min_size / 16 + 32, min_size);
    av_freep(ptr);
    val = zero_realloc ? av_mallocz(min_size) : av_malloc(min_size);
    memcpy(ptr, &val, sizeof(val));
    if (!val)
        min_size = 0;
    *size = min_size;
    return 1;
}

11 、 av_fast_mallocz

av_fast_mallocz() allocates memory quickly, and the memory is zeroed:

void av_fast_mallocz(void *ptr, unsigned int *size, size_t min_size)
{
    ff_fast_malloc(ptr, size, min_size, 1);
}

Second, the memory copy

1 、 av_strdup

av_strdup() is used to reallocate memory and copy strings:

char *av_strdup(const char *s)
{
    char *ptr = NULL;
    if (s) {
        size_t len = strlen(s) + 1;
        ptr = av_realloc(NULL, len);
        if (ptr)
            memcpy(ptr, s, len);
    }
    return ptr;
}

2 、 av_strndup

av_strndup() is used to allocate memory of the specified size and copy the string. First, use memchr() to obtain the effective string length, then use av_realloc() to reallocate the memory, and then use memcpy() to copy the string:

char *av_strndup(const char *s, size_t len)
{
    char *ret = NULL, *end;

    if (!s)
        return NULL;

    end = memchr(s, 0, len);
    if (end)
        len = end - s;

    ret = av_realloc(NULL, len + 1);
    if (!ret)
        return NULL;

    memcpy(ret, s, len);
    ret[len] = 0;
    return ret;
}

3 、 av_memdup

av_memdup() is used for memory allocation and memory copying. First use av_malloc() to allocate memory, and then use memcpy() to copy memory:

void *av_memdup(const void *p, size_t size)
{
    void *ptr = NULL;
    if (p) {
        ptr = av_malloc(size);
        if (ptr)
            memcpy(ptr, p, size);
    }
    return ptr;
}

4 、 av_memcpy_backptr

av_memcpy_backptr() is used for memory copying, similar to the system-provided memcpy(), and considers 16-bit, 24-bit, and 32-bit memory alignment:

void av_memcpy_backptr(uint8_t *dst, int back, int cnt)
{
    const uint8_t *src = &dst[-back];
    if (!back)
        return;

    if (back == 1) {
        memset(dst, *src, cnt);
    } else if (back == 2) {
        fill16(dst, cnt);
    } else if (back == 3) {
        fill24(dst, cnt);
    } else if (back == 4) {
        fill32(dst, cnt);
    } else {
        if (cnt >= 16) {
            int blocklen = back;
            while (cnt > blocklen) {
                memcpy(dst, src, blocklen);
                dst       += blocklen;
                cnt       -= blocklen;
                blocklen <<= 1;
            }
            memcpy(dst, src, cnt);
            return;
        }
        if (cnt >= 8) {
            AV_COPY32U(dst,     src);
            AV_COPY32U(dst + 4, src + 4);
            src += 8;
            dst += 8;
            cnt -= 8;
        }
        if (cnt >= 4) {
            AV_COPY32U(dst, src);
            src += 4;
            dst += 4;
            cnt -= 4;
        }
        if (cnt >= 2) {
            AV_COPY16U(dst, src);
            src += 2;
            dst += 2;
            cnt -= 2;
        }
        if (cnt)
            *dst = *src;
    }
}

3. Memory release

1 、 av_free

av_free() is used to release memory blocks, mainly by calling system free() to release. If the macro defines an aligned allocation, then to align the free:

void av_free(void *ptr)
{
#if HAVE_ALIGNED_MALLOC
    _aligned_free(ptr);
#else
    free(ptr);
#endif
}

2 、 av_freep

av_freep() is used to release the memory pointer, first back up the memory pointer, then clear the pointer address, and then release the memory:

void av_freep(void *arg)
{
    void *val;

    memcpy(&val, arg, sizeof(val));
    memcpy(arg, &(void *){ NULL }, sizeof(val));
    av_free(val);
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324449266&siteId=291194637