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 <stdint.h> 20#include <string.h> 21 22#include "atomic.h" 23#include "buffer_internal.h" 24#include "common.h" 25#include "mem.h" 26 27AVBufferRef *av_buffer_create(uint8_t *data, int size, 28 void (*free)(void *opaque, uint8_t *data), 29 void *opaque, int flags) 30{ 31 AVBufferRef *ref = NULL; 32 AVBuffer *buf = NULL; 33 34 buf = av_mallocz(sizeof(*buf)); 35 if (!buf) 36 return NULL; 37 38 buf->data = data; 39 buf->size = size; 40 buf->free = free ? free : av_buffer_default_free; 41 buf->opaque = opaque; 42 buf->refcount = 1; 43 44 if (flags & AV_BUFFER_FLAG_READONLY) 45 buf->flags |= BUFFER_FLAG_READONLY; 46 47 ref = av_mallocz(sizeof(*ref)); 48 if (!ref) { 49 av_freep(&buf); 50 return NULL; 51 } 52 53 ref->buffer = buf; 54 ref->data = data; 55 ref->size = size; 56 57 return ref; 58} 59 60void av_buffer_default_free(void *opaque, uint8_t *data) 61{ 62 av_free(data); 63} 64 65AVBufferRef *av_buffer_alloc(int size) 66{ 67 AVBufferRef *ret = NULL; 68 uint8_t *data = NULL; 69 70 data = av_malloc(size); 71 if (!data) 72 return NULL; 73 74 ret = av_buffer_create(data, size, av_buffer_default_free, NULL, 0); 75 if (!ret) 76 av_freep(&data); 77 78 return ret; 79} 80 81AVBufferRef *av_buffer_allocz(int size) 82{ 83 AVBufferRef *ret = av_buffer_alloc(size); 84 if (!ret) 85 return NULL; 86 87 memset(ret->data, 0, size); 88 return ret; 89} 90 91AVBufferRef *av_buffer_ref(AVBufferRef *buf) 92{ 93 AVBufferRef *ret = av_mallocz(sizeof(*ret)); 94 95 if (!ret) 96 return NULL; 97 98 *ret = *buf; 99 100 avpriv_atomic_int_add_and_fetch(&buf->buffer->refcount, 1); 101 102 return ret; 103} 104 105void av_buffer_unref(AVBufferRef **buf) 106{ 107 AVBuffer *b; 108 109 if (!buf || !*buf) 110 return; 111 b = (*buf)->buffer; 112 av_freep(buf); 113 114 if (!avpriv_atomic_int_add_and_fetch(&b->refcount, -1)) { 115 b->free(b->opaque, b->data); 116 av_freep(&b); 117 } 118} 119 120int av_buffer_is_writable(const AVBufferRef *buf) 121{ 122 if (buf->buffer->flags & AV_BUFFER_FLAG_READONLY) 123 return 0; 124 125 return avpriv_atomic_int_get(&buf->buffer->refcount) == 1; 126} 127 128void *av_buffer_get_opaque(const AVBufferRef *buf) 129{ 130 return buf->buffer->opaque; 131} 132 133int av_buffer_get_ref_count(const AVBufferRef *buf) 134{ 135 return buf->buffer->refcount; 136} 137 138int av_buffer_make_writable(AVBufferRef **pbuf) 139{ 140 AVBufferRef *newbuf, *buf = *pbuf; 141 142 if (av_buffer_is_writable(buf)) 143 return 0; 144 145 newbuf = av_buffer_alloc(buf->size); 146 if (!newbuf) 147 return AVERROR(ENOMEM); 148 149 memcpy(newbuf->data, buf->data, buf->size); 150 av_buffer_unref(pbuf); 151 *pbuf = newbuf; 152 153 return 0; 154} 155 156int av_buffer_realloc(AVBufferRef **pbuf, int size) 157{ 158 AVBufferRef *buf = *pbuf; 159 uint8_t *tmp; 160 161 if (!buf) { 162 /* allocate a new buffer with av_realloc(), so it will be reallocatable 163 * later */ 164 uint8_t *data = av_realloc(NULL, size); 165 if (!data) 166 return AVERROR(ENOMEM); 167 168 buf = av_buffer_create(data, size, av_buffer_default_free, NULL, 0); 169 if (!buf) { 170 av_freep(&data); 171 return AVERROR(ENOMEM); 172 } 173 174 buf->buffer->flags |= BUFFER_FLAG_REALLOCATABLE; 175 *pbuf = buf; 176 177 return 0; 178 } else if (buf->size == size) 179 return 0; 180 181 if (!(buf->buffer->flags & BUFFER_FLAG_REALLOCATABLE) || 182 !av_buffer_is_writable(buf)) { 183 /* cannot realloc, allocate a new reallocable buffer and copy data */ 184 AVBufferRef *new = NULL; 185 186 av_buffer_realloc(&new, size); 187 if (!new) 188 return AVERROR(ENOMEM); 189 190 memcpy(new->data, buf->data, FFMIN(size, buf->size)); 191 192 av_buffer_unref(pbuf); 193 *pbuf = new; 194 return 0; 195 } 196 197 tmp = av_realloc(buf->buffer->data, size); 198 if (!tmp) 199 return AVERROR(ENOMEM); 200 201 buf->buffer->data = buf->data = tmp; 202 buf->buffer->size = buf->size = size; 203 return 0; 204} 205 206AVBufferPool *av_buffer_pool_init(int size, AVBufferRef* (*alloc)(int size)) 207{ 208 AVBufferPool *pool = av_mallocz(sizeof(*pool)); 209 if (!pool) 210 return NULL; 211 212 pool->size = size; 213 pool->alloc = alloc ? alloc : av_buffer_alloc; 214 215 avpriv_atomic_int_set(&pool->refcount, 1); 216 217 return pool; 218} 219 220/* 221 * This function gets called when the pool has been uninited and 222 * all the buffers returned to it. 223 */ 224static void buffer_pool_free(AVBufferPool *pool) 225{ 226 while (pool->pool) { 227 BufferPoolEntry *buf = pool->pool; 228 pool->pool = buf->next; 229 230 buf->free(buf->opaque, buf->data); 231 av_freep(&buf); 232 } 233 av_freep(&pool); 234} 235 236void av_buffer_pool_uninit(AVBufferPool **ppool) 237{ 238 AVBufferPool *pool; 239 240 if (!ppool || !*ppool) 241 return; 242 pool = *ppool; 243 *ppool = NULL; 244 245 if (!avpriv_atomic_int_add_and_fetch(&pool->refcount, -1)) 246 buffer_pool_free(pool); 247} 248 249/* remove the whole buffer list from the pool and return it */ 250static BufferPoolEntry *get_pool(AVBufferPool *pool) 251{ 252 BufferPoolEntry *cur = *(void * volatile *)&pool->pool, *last = NULL; 253 254 while (cur != last) { 255 last = cur; 256 cur = avpriv_atomic_ptr_cas((void * volatile *)&pool->pool, last, NULL); 257 if (!cur) 258 return NULL; 259 } 260 261 return cur; 262} 263 264static void add_to_pool(BufferPoolEntry *buf) 265{ 266 AVBufferPool *pool; 267 BufferPoolEntry *cur, *end = buf; 268 269 if (!buf) 270 return; 271 pool = buf->pool; 272 273 while (end->next) 274 end = end->next; 275 276 while (avpriv_atomic_ptr_cas((void * volatile *)&pool->pool, NULL, buf)) { 277 /* pool is not empty, retrieve it and append it to our list */ 278 cur = get_pool(pool); 279 end->next = cur; 280 while (end->next) 281 end = end->next; 282 } 283} 284 285static void pool_release_buffer(void *opaque, uint8_t *data) 286{ 287 BufferPoolEntry *buf = opaque; 288 AVBufferPool *pool = buf->pool; 289 290 if(CONFIG_MEMORY_POISONING) 291 memset(buf->data, FF_MEMORY_POISON, pool->size); 292 293 add_to_pool(buf); 294 if (!avpriv_atomic_int_add_and_fetch(&pool->refcount, -1)) 295 buffer_pool_free(pool); 296} 297 298/* allocate a new buffer and override its free() callback so that 299 * it is returned to the pool on free */ 300static AVBufferRef *pool_alloc_buffer(AVBufferPool *pool) 301{ 302 BufferPoolEntry *buf; 303 AVBufferRef *ret; 304 305 ret = pool->alloc(pool->size); 306 if (!ret) 307 return NULL; 308 309 buf = av_mallocz(sizeof(*buf)); 310 if (!buf) { 311 av_buffer_unref(&ret); 312 return NULL; 313 } 314 315 buf->data = ret->buffer->data; 316 buf->opaque = ret->buffer->opaque; 317 buf->free = ret->buffer->free; 318 buf->pool = pool; 319 320 ret->buffer->opaque = buf; 321 ret->buffer->free = pool_release_buffer; 322 323 avpriv_atomic_int_add_and_fetch(&pool->refcount, 1); 324 avpriv_atomic_int_add_and_fetch(&pool->nb_allocated, 1); 325 326 return ret; 327} 328 329AVBufferRef *av_buffer_pool_get(AVBufferPool *pool) 330{ 331 AVBufferRef *ret; 332 BufferPoolEntry *buf; 333 334 /* check whether the pool is empty */ 335 buf = get_pool(pool); 336 if (!buf && pool->refcount <= pool->nb_allocated) { 337 av_log(NULL, AV_LOG_DEBUG, "Pool race dectected, spining to avoid overallocation and eventual OOM\n"); 338 while (!buf && avpriv_atomic_int_get(&pool->refcount) <= avpriv_atomic_int_get(&pool->nb_allocated)) 339 buf = get_pool(pool); 340 } 341 342 if (!buf) 343 return pool_alloc_buffer(pool); 344 345 /* keep the first entry, return the rest of the list to the pool */ 346 add_to_pool(buf->next); 347 buf->next = NULL; 348 349 ret = av_buffer_create(buf->data, pool->size, pool_release_buffer, 350 buf, 0); 351 if (!ret) { 352 add_to_pool(buf); 353 return NULL; 354 } 355 avpriv_atomic_int_add_and_fetch(&pool->refcount, 1); 356 357 return ret; 358} 359