1/* 2 * This file is part of FFmpeg. 3 * 4 * FFmpeg is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation; either 7 * version 2.1 of the License, or (at your option) any later version. 8 * 9 * FFmpeg is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public 15 * License along with FFmpeg; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 */ 18 19#include <stdatomic.h> 20#include <stdint.h> 21#include <string.h> 22 23#include "avassert.h" 24#include "buffer_internal.h" 25#include "common.h" 26#include "mem.h" 27#include "thread.h" 28 29static AVBufferRef *buffer_create(AVBuffer *buf, uint8_t *data, size_t size, 30 void (*free)(void *opaque, uint8_t *data), 31 void *opaque, int flags) 32{ 33 AVBufferRef *ref = NULL; 34 35 buf->data = data; 36 buf->size = size; 37 buf->free = free ? free : av_buffer_default_free; 38 buf->opaque = opaque; 39 40 atomic_init(&buf->refcount, 1); 41 42 buf->flags = flags; 43 44 ref = av_mallocz(sizeof(*ref)); 45 if (!ref) 46 return NULL; 47 48 ref->buffer = buf; 49 ref->data = data; 50 ref->size = size; 51 52 return ref; 53} 54 55AVBufferRef *av_buffer_create(uint8_t *data, size_t size, 56 void (*free)(void *opaque, uint8_t *data), 57 void *opaque, int flags) 58{ 59 AVBufferRef *ret; 60 AVBuffer *buf = av_mallocz(sizeof(*buf)); 61 if (!buf) 62 return NULL; 63 64 ret = buffer_create(buf, data, size, free, opaque, flags); 65 if (!ret) { 66 av_free(buf); 67 return NULL; 68 } 69 return ret; 70} 71 72void av_buffer_default_free(void *opaque, uint8_t *data) 73{ 74 av_free(data); 75} 76 77AVBufferRef *av_buffer_alloc(size_t size) 78{ 79 AVBufferRef *ret = NULL; 80 uint8_t *data = NULL; 81 82 data = av_malloc(size); 83 if (!data) 84 return NULL; 85 86 ret = av_buffer_create(data, size, av_buffer_default_free, NULL, 0); 87 if (!ret) 88 av_freep(&data); 89 90 return ret; 91} 92 93AVBufferRef *av_buffer_allocz(size_t size) 94{ 95 AVBufferRef *ret = av_buffer_alloc(size); 96 if (!ret) 97 return NULL; 98 99 memset(ret->data, 0, size); 100 return ret; 101} 102 103AVBufferRef *av_buffer_ref(const AVBufferRef *buf) 104{ 105 AVBufferRef *ret = av_mallocz(sizeof(*ret)); 106 107 if (!ret) 108 return NULL; 109 110 *ret = *buf; 111 112 atomic_fetch_add_explicit(&buf->buffer->refcount, 1, memory_order_relaxed); 113 114 return ret; 115} 116 117static void buffer_replace(AVBufferRef **dst, AVBufferRef **src) 118{ 119 AVBuffer *b; 120 121 b = (*dst)->buffer; 122 123 if (src) { 124 **dst = **src; 125 av_freep(src); 126 } else 127 av_freep(dst); 128 129 if (atomic_fetch_sub_explicit(&b->refcount, 1, memory_order_acq_rel) == 1) { 130 /* b->free below might already free the structure containing *b, 131 * so we have to read the flag now to avoid use-after-free. */ 132 int free_avbuffer = !(b->flags_internal & BUFFER_FLAG_NO_FREE); 133 b->free(b->opaque, b->data); 134 if (free_avbuffer) 135 av_free(b); 136 } 137} 138 139void av_buffer_unref(AVBufferRef **buf) 140{ 141 if (!buf || !*buf) 142 return; 143 144 buffer_replace(buf, NULL); 145} 146 147int av_buffer_is_writable(const AVBufferRef *buf) 148{ 149 if (buf->buffer->flags & AV_BUFFER_FLAG_READONLY) 150 return 0; 151 152 return atomic_load(&buf->buffer->refcount) == 1; 153} 154 155void *av_buffer_get_opaque(const AVBufferRef *buf) 156{ 157 return buf->buffer->opaque; 158} 159 160int av_buffer_get_ref_count(const AVBufferRef *buf) 161{ 162 return atomic_load(&buf->buffer->refcount); 163} 164 165int av_buffer_make_writable(AVBufferRef **pbuf) 166{ 167 AVBufferRef *newbuf, *buf = *pbuf; 168 169 if (av_buffer_is_writable(buf)) 170 return 0; 171 172 newbuf = av_buffer_alloc(buf->size); 173 if (!newbuf) 174 return AVERROR(ENOMEM); 175 176 memcpy(newbuf->data, buf->data, buf->size); 177 178 buffer_replace(pbuf, &newbuf); 179 180 return 0; 181} 182 183int av_buffer_realloc(AVBufferRef **pbuf, size_t size) 184{ 185 AVBufferRef *buf = *pbuf; 186 uint8_t *tmp; 187 int ret; 188 189 if (!buf) { 190 /* allocate a new buffer with av_realloc(), so it will be reallocatable 191 * later */ 192 uint8_t *data = av_realloc(NULL, size); 193 if (!data) 194 return AVERROR(ENOMEM); 195 196 buf = av_buffer_create(data, size, av_buffer_default_free, NULL, 0); 197 if (!buf) { 198 av_freep(&data); 199 return AVERROR(ENOMEM); 200 } 201 202 buf->buffer->flags_internal |= BUFFER_FLAG_REALLOCATABLE; 203 *pbuf = buf; 204 205 return 0; 206 } else if (buf->size == size) 207 return 0; 208 209 if (!(buf->buffer->flags_internal & BUFFER_FLAG_REALLOCATABLE) || 210 !av_buffer_is_writable(buf) || buf->data != buf->buffer->data) { 211 /* cannot realloc, allocate a new reallocable buffer and copy data */ 212 AVBufferRef *new = NULL; 213 214 ret = av_buffer_realloc(&new, size); 215 if (ret < 0) 216 return ret; 217 218 memcpy(new->data, buf->data, FFMIN(size, buf->size)); 219 220 buffer_replace(pbuf, &new); 221 return 0; 222 } 223 224 tmp = av_realloc(buf->buffer->data, size); 225 if (!tmp) 226 return AVERROR(ENOMEM); 227 228 buf->buffer->data = buf->data = tmp; 229 buf->buffer->size = buf->size = size; 230 return 0; 231} 232 233int av_buffer_replace(AVBufferRef **pdst, const AVBufferRef *src) 234{ 235 AVBufferRef *dst = *pdst; 236 AVBufferRef *tmp; 237 238 if (!src) { 239 av_buffer_unref(pdst); 240 return 0; 241 } 242 243 if (dst && dst->buffer == src->buffer) { 244 /* make sure the data pointers match */ 245 dst->data = src->data; 246 dst->size = src->size; 247 return 0; 248 } 249 250 tmp = av_buffer_ref(src); 251 if (!tmp) 252 return AVERROR(ENOMEM); 253 254 av_buffer_unref(pdst); 255 *pdst = tmp; 256 return 0; 257} 258 259AVBufferPool *av_buffer_pool_init2(size_t size, void *opaque, 260 AVBufferRef* (*alloc)(void *opaque, size_t size), 261 void (*pool_free)(void *opaque)) 262{ 263 AVBufferPool *pool = av_mallocz(sizeof(*pool)); 264 if (!pool) 265 return NULL; 266 267 ff_mutex_init(&pool->mutex, NULL); 268 269 pool->size = size; 270 pool->opaque = opaque; 271 pool->alloc2 = alloc; 272 pool->alloc = av_buffer_alloc; // fallback 273 pool->pool_free = pool_free; 274 275 atomic_init(&pool->refcount, 1); 276 277 return pool; 278} 279 280AVBufferPool *av_buffer_pool_init(size_t size, AVBufferRef* (*alloc)(size_t size)) 281{ 282 AVBufferPool *pool = av_mallocz(sizeof(*pool)); 283 if (!pool) 284 return NULL; 285 286 ff_mutex_init(&pool->mutex, NULL); 287 288 pool->size = size; 289 pool->alloc = alloc ? alloc : av_buffer_alloc; 290 291 atomic_init(&pool->refcount, 1); 292 293 return pool; 294} 295 296static void buffer_pool_flush(AVBufferPool *pool) 297{ 298 while (pool->pool) { 299 BufferPoolEntry *buf = pool->pool; 300 pool->pool = buf->next; 301 302 buf->free(buf->opaque, buf->data); 303 av_freep(&buf); 304 } 305} 306 307/* 308 * This function gets called when the pool has been uninited and 309 * all the buffers returned to it. 310 */ 311static void buffer_pool_free(AVBufferPool *pool) 312{ 313 buffer_pool_flush(pool); 314 ff_mutex_destroy(&pool->mutex); 315 316 if (pool->pool_free) 317 pool->pool_free(pool->opaque); 318 319 av_freep(&pool); 320} 321 322void av_buffer_pool_uninit(AVBufferPool **ppool) 323{ 324 AVBufferPool *pool; 325 326 if (!ppool || !*ppool) 327 return; 328 pool = *ppool; 329 *ppool = NULL; 330 331 ff_mutex_lock(&pool->mutex); 332 buffer_pool_flush(pool); 333 ff_mutex_unlock(&pool->mutex); 334 335 if (atomic_fetch_sub_explicit(&pool->refcount, 1, memory_order_acq_rel) == 1) 336 buffer_pool_free(pool); 337} 338 339static void pool_release_buffer(void *opaque, uint8_t *data) 340{ 341 BufferPoolEntry *buf = opaque; 342 AVBufferPool *pool = buf->pool; 343 344 if(CONFIG_MEMORY_POISONING) 345 memset(buf->data, FF_MEMORY_POISON, pool->size); 346 347 ff_mutex_lock(&pool->mutex); 348 buf->next = pool->pool; 349 pool->pool = buf; 350 ff_mutex_unlock(&pool->mutex); 351 352 if (atomic_fetch_sub_explicit(&pool->refcount, 1, memory_order_acq_rel) == 1) 353 buffer_pool_free(pool); 354} 355 356/* allocate a new buffer and override its free() callback so that 357 * it is returned to the pool on free */ 358static AVBufferRef *pool_alloc_buffer(AVBufferPool *pool) 359{ 360 BufferPoolEntry *buf; 361 AVBufferRef *ret; 362 363 av_assert0(pool->alloc || pool->alloc2); 364 365 ret = pool->alloc2 ? pool->alloc2(pool->opaque, pool->size) : 366 pool->alloc(pool->size); 367 if (!ret) 368 return NULL; 369 370 buf = av_mallocz(sizeof(*buf)); 371 if (!buf) { 372 av_buffer_unref(&ret); 373 return NULL; 374 } 375 376 buf->data = ret->buffer->data; 377 buf->opaque = ret->buffer->opaque; 378 buf->free = ret->buffer->free; 379 buf->pool = pool; 380 381 ret->buffer->opaque = buf; 382 ret->buffer->free = pool_release_buffer; 383 384 return ret; 385} 386 387AVBufferRef *av_buffer_pool_get(AVBufferPool *pool) 388{ 389 AVBufferRef *ret; 390 BufferPoolEntry *buf; 391 392 ff_mutex_lock(&pool->mutex); 393 buf = pool->pool; 394 if (buf) { 395 memset(&buf->buffer, 0, sizeof(buf->buffer)); 396 ret = buffer_create(&buf->buffer, buf->data, pool->size, 397 pool_release_buffer, buf, 0); 398 if (ret) { 399 pool->pool = buf->next; 400 buf->next = NULL; 401 buf->buffer.flags_internal |= BUFFER_FLAG_NO_FREE; 402 } 403 } else { 404 ret = pool_alloc_buffer(pool); 405 } 406 ff_mutex_unlock(&pool->mutex); 407 408 if (ret) 409 atomic_fetch_add_explicit(&pool->refcount, 1, memory_order_relaxed); 410 411 return ret; 412} 413 414void *av_buffer_pool_buffer_get_opaque(const AVBufferRef *ref) 415{ 416 BufferPoolEntry *buf = ref->buffer->opaque; 417 av_assert0(buf); 418 return buf->opaque; 419} 420