• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
buffer_create(AVBuffer * buf,uint8_t * data,size_t size,void (* free)(void * opaque,uint8_t * data),void * opaque,int flags)29 static 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 
av_buffer_create(uint8_t * data,size_t size,void (* free)(void * opaque,uint8_t * data),void * opaque,int flags)55 AVBufferRef *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 
av_buffer_default_free(void * opaque,uint8_t * data)72 void av_buffer_default_free(void *opaque, uint8_t *data)
73 {
74     av_free(data);
75 }
76 
av_buffer_alloc(size_t size)77 AVBufferRef *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 
av_buffer_allocz(size_t size)93 AVBufferRef *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 
av_buffer_ref(const AVBufferRef * buf)103 AVBufferRef *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 
buffer_replace(AVBufferRef ** dst,AVBufferRef ** src)117 static 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 
av_buffer_unref(AVBufferRef ** buf)139 void av_buffer_unref(AVBufferRef **buf)
140 {
141     if (!buf || !*buf)
142         return;
143 
144     buffer_replace(buf, NULL);
145 }
146 
av_buffer_is_writable(const AVBufferRef * buf)147 int 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 
av_buffer_get_opaque(const AVBufferRef * buf)155 void *av_buffer_get_opaque(const AVBufferRef *buf)
156 {
157     return buf->buffer->opaque;
158 }
159 
av_buffer_get_ref_count(const AVBufferRef * buf)160 int av_buffer_get_ref_count(const AVBufferRef *buf)
161 {
162     return atomic_load(&buf->buffer->refcount);
163 }
164 
av_buffer_make_writable(AVBufferRef ** pbuf)165 int 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 
av_buffer_realloc(AVBufferRef ** pbuf,size_t size)183 int 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 
av_buffer_replace(AVBufferRef ** pdst,const AVBufferRef * src)233 int 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 
av_buffer_pool_init2(size_t size,void * opaque,AVBufferRef * (* alloc)(void * opaque,size_t size),void (* pool_free)(void * opaque))259 AVBufferPool *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 
av_buffer_pool_init(size_t size,AVBufferRef * (* alloc)(size_t size))280 AVBufferPool *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 
buffer_pool_flush(AVBufferPool * pool)296 static 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  */
buffer_pool_free(AVBufferPool * pool)311 static 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 
av_buffer_pool_uninit(AVBufferPool ** ppool)322 void 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 
pool_release_buffer(void * opaque,uint8_t * data)339 static 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 */
pool_alloc_buffer(AVBufferPool * pool)358 static 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 
av_buffer_pool_get(AVBufferPool * pool)387 AVBufferRef *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 
av_buffer_pool_buffer_get_opaque(const AVBufferRef * ref)414 void *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