Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4349 | Serge | 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 |
||
20 | #include |
||
21 | |||
22 | #include "atomic.h" |
||
23 | #include "buffer_internal.h" |
||
24 | #include "common.h" |
||
25 | #include "mem.h" |
||
26 | |||
27 | AVBufferRef *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 | |||
60 | void av_buffer_default_free(void *opaque, uint8_t *data) |
||
61 | { |
||
62 | av_free(data); |
||
63 | } |
||
64 | |||
65 | AVBufferRef *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 | |||
81 | AVBufferRef *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 | |||
91 | AVBufferRef *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 | |||
105 | void 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 | |||
120 | int 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 | |||
128 | void *av_buffer_get_opaque(const AVBufferRef *buf) |
||
129 | { |
||
130 | return buf->buffer->opaque; |
||
131 | } |
||
132 | |||
133 | int av_buffer_get_ref_count(const AVBufferRef *buf) |
||
134 | { |
||
135 | return buf->buffer->refcount; |
||
136 | } |
||
137 | |||
138 | int 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 | |||
156 | int 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 | |||
206 | AVBufferPool *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 | */ |
||
224 | static 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 | |||
236 | void 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 */ |
||
250 | static 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 | |||
264 | static 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 | |||
285 | static 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 */ |
||
300 | static 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 | |||
329 | AVBufferRef *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 | }=>=> |