| /* |
| * This file is part of FFmpeg. |
| * |
| * FFmpeg is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * FFmpeg is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with FFmpeg; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include <limits.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| #include "libavutil/buffer.h" |
| #include "libavutil/mem.h" |
| |
| static int custom_free_called; |
| static int pool_free_called; |
| static int pool_alloc2_called; |
| |
| static void custom_free(void *opaque, uint8_t *data) |
| { |
| custom_free_called = 1; |
| av_free(data); |
| } |
| |
| static AVBufferRef *pool_alloc2(void *opaque, size_t size) |
| { |
| pool_alloc2_called = 1; |
| return av_buffer_alloc(size); |
| } |
| |
| static void pool_free_cb(void *opaque) |
| { |
| pool_free_called = 1; |
| } |
| |
| int main(void) |
| { |
| AVBufferRef *buf, *buf2; |
| AVBufferPool *pool; |
| |
| /* av_buffer_alloc */ |
| printf("Testing av_buffer_alloc()\n"); |
| buf = av_buffer_alloc(64); |
| if (buf) { |
| printf("alloc: size=%zu data=%s\n", buf->size, buf->data ? "set" : "null"); |
| printf("writable: %d\n", av_buffer_is_writable(buf)); |
| printf("refcount: %d\n", av_buffer_get_ref_count(buf)); |
| av_buffer_unref(&buf); |
| printf("after unref: %s\n", buf == NULL ? "null" : "leaked"); |
| } |
| |
| /* av_buffer_allocz */ |
| printf("\nTesting av_buffer_allocz()\n"); |
| buf = av_buffer_allocz(16); |
| if (buf) { |
| int zeroed = 1; |
| for (int i = 0; i < 16; i++) |
| if (buf->data[i] != 0) zeroed = 0; |
| printf("allocz: zeroed=%s\n", zeroed ? "yes" : "no"); |
| av_buffer_unref(&buf); |
| } |
| |
| /* av_buffer_create with custom free */ |
| printf("\nTesting av_buffer_create()\n"); |
| { |
| uint8_t *data = av_malloc(32); |
| if (data) { |
| custom_free_called = 0; |
| buf = av_buffer_create(data, 32, custom_free, NULL, 0); |
| if (buf) { |
| printf("create: size=%zu\n", buf->size); |
| printf("opaque: %s\n", |
| av_buffer_get_opaque(buf) == NULL ? "null" : "set"); |
| av_buffer_unref(&buf); |
| printf("custom_free called: %s\n", |
| custom_free_called ? "yes" : "no"); |
| } else { |
| av_free(data); |
| } |
| } |
| } |
| |
| /* av_buffer_create with READONLY flag */ |
| printf("\nTesting AV_BUFFER_FLAG_READONLY\n"); |
| { |
| uint8_t *data = av_malloc(16); |
| if (data) { |
| buf = av_buffer_create(data, 16, custom_free, NULL, |
| AV_BUFFER_FLAG_READONLY); |
| if (buf) { |
| printf("readonly writable: %d\n", av_buffer_is_writable(buf)); |
| av_buffer_unref(&buf); |
| } else { |
| av_free(data); |
| } |
| } |
| } |
| |
| /* av_buffer_ref and refcounting */ |
| printf("\nTesting av_buffer_ref()\n"); |
| buf = av_buffer_alloc(32); |
| if (buf) { |
| buf->data[0] = 0xAB; |
| buf2 = av_buffer_ref(buf); |
| if (buf2) { |
| printf("ref: refcount=%d\n", av_buffer_get_ref_count(buf)); |
| printf("shared data: %s\n", |
| buf2->data[0] == 0xAB ? "yes" : "no"); |
| printf("writable after ref: %d\n", av_buffer_is_writable(buf)); |
| av_buffer_unref(&buf2); |
| printf("refcount after unref: %d\n", av_buffer_get_ref_count(buf)); |
| printf("writable after unref: %d\n", av_buffer_is_writable(buf)); |
| } |
| av_buffer_unref(&buf); |
| } |
| |
| /* av_buffer_make_writable */ |
| printf("\nTesting av_buffer_make_writable()\n"); |
| buf = av_buffer_alloc(16); |
| if (buf) { |
| buf->data[0] = 0xCD; |
| buf2 = av_buffer_ref(buf); |
| if (buf2) { |
| int ret = av_buffer_make_writable(&buf2); |
| printf("make_writable ret: %d\n", ret >= 0); |
| printf("data preserved: %s\n", |
| buf2->data[0] == 0xCD ? "yes" : "no"); |
| printf("now writable: %d\n", av_buffer_is_writable(buf2)); |
| printf("original still valid: %s\n", |
| buf->data[0] == 0xCD ? "yes" : "no"); |
| av_buffer_unref(&buf2); |
| } |
| av_buffer_unref(&buf); |
| } |
| |
| /* av_buffer_realloc */ |
| printf("\nTesting av_buffer_realloc()\n"); |
| buf = av_buffer_alloc(16); |
| if (buf) { |
| memset(buf->data, 0xEF, 16); |
| av_buffer_realloc(&buf, 32); |
| if (buf) { |
| printf("realloc: size=%zu\n", buf->size); |
| printf("data preserved: %s\n", |
| buf->data[0] == 0xEF ? "yes" : "no"); |
| } |
| av_buffer_unref(&buf); |
| } |
| /* realloc from NULL */ |
| buf = NULL; |
| av_buffer_realloc(&buf, 8); |
| printf("realloc from null: %s\n", buf ? "OK" : "FAIL"); |
| av_buffer_unref(&buf); |
| |
| /* av_buffer_replace */ |
| printf("\nTesting av_buffer_replace()\n"); |
| buf = av_buffer_alloc(8); |
| buf2 = av_buffer_alloc(8); |
| if (buf && buf2) { |
| buf->data[0] = 0x11; |
| buf2->data[0] = 0x22; |
| av_buffer_replace(&buf, buf2); |
| printf("replace: data=0x%02x\n", buf->data[0]); |
| printf("refcount: %d\n", av_buffer_get_ref_count(buf2)); |
| } |
| av_buffer_unref(&buf); |
| av_buffer_unref(&buf2); |
| |
| /* replace with NULL */ |
| buf = av_buffer_alloc(8); |
| if (buf) { |
| av_buffer_replace(&buf, NULL); |
| printf("replace with null: %s\n", buf == NULL ? "OK" : "FAIL"); |
| } |
| |
| /* av_buffer_pool */ |
| printf("\nTesting av_buffer_pool()\n"); |
| pool = av_buffer_pool_init(64, NULL); |
| if (pool) { |
| buf = av_buffer_pool_get(pool); |
| if (buf) { |
| printf("pool get: size=%zu\n", buf->size); |
| av_buffer_unref(&buf); |
| } |
| /* get again -- should reuse the released buffer */ |
| buf = av_buffer_pool_get(pool); |
| if (buf) { |
| printf("pool reuse: size=%zu\n", buf->size); |
| av_buffer_unref(&buf); |
| } |
| av_buffer_pool_uninit(&pool); |
| printf("pool uninit: %s\n", pool == NULL ? "OK" : "FAIL"); |
| } |
| |
| /* av_buffer_pool_init2 with custom alloc and pool_free callbacks */ |
| printf("\nTesting av_buffer_pool_init2()\n"); |
| pool_alloc2_called = 0; |
| pool_free_called = 0; |
| pool = av_buffer_pool_init2(64, NULL, pool_alloc2, pool_free_cb); |
| if (pool) { |
| buf = av_buffer_pool_get(pool); |
| if (buf) { |
| printf("pool2 get: size=%zu\n", buf->size); |
| printf("alloc2 called: %s\n", pool_alloc2_called ? "yes" : "no"); |
| printf("pool_buffer_get_opaque: %s\n", |
| av_buffer_pool_buffer_get_opaque(buf) == NULL ? "null" : "set"); |
| av_buffer_unref(&buf); |
| } |
| av_buffer_pool_uninit(&pool); |
| printf("pool_free called: %s\n", pool_free_called ? "yes" : "no"); |
| } |
| |
| /* OOM paths via av_max_alloc */ |
| printf("\nTesting OOM paths\n"); |
| av_max_alloc(1); |
| buf = av_buffer_alloc(64); |
| printf("alloc OOM: %s\n", buf ? "FAIL" : "OK"); |
| av_buffer_unref(&buf); |
| buf = av_buffer_allocz(64); |
| printf("allocz OOM: %s\n", buf ? "FAIL" : "OK"); |
| av_buffer_unref(&buf); |
| pool = av_buffer_pool_init(64, NULL); |
| printf("pool init OOM: %s\n", pool ? "FAIL" : "OK"); |
| av_buffer_pool_uninit(&pool); |
| av_max_alloc(INT_MAX); |
| |
| buf = av_buffer_alloc(16); |
| if (buf) { |
| av_max_alloc(1); |
| buf2 = av_buffer_ref(buf); |
| printf("ref OOM: %s\n", buf2 ? "FAIL" : "OK"); |
| av_buffer_unref(&buf2); |
| printf("realloc OOM: %s\n", |
| av_buffer_realloc(&buf, 1024) < 0 ? "OK" : "FAIL"); |
| av_max_alloc(INT_MAX); |
| av_buffer_unref(&buf); |
| } |
| |
| return 0; |
| } |