xref: /third_party/ffmpeg/libavutil/buffer.c (revision cabdff1a)
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