| /* |
| * This file is part of FFmpeg. |
| * |
| * FFmpeg is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * FFmpeg is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with FFmpeg; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include "avassert.h" |
| #include "container_fifo.h" |
| #include "error.h" |
| #include "fifo.h" |
| #include "frame.h" |
| #include "mem.h" |
| #include "refstruct.h" |
| |
| struct AVContainerFifo { |
| AVFifo *fifo; |
| AVRefStructPool *pool; |
| |
| void *opaque; |
| void* (*container_alloc)(void *opaque); |
| void (*container_reset)(void *opaque, void *obj); |
| void (*container_free) (void *opaque, void *obj); |
| int (*fifo_transfer) (void *opaque, void *dst, void *src, unsigned flags); |
| |
| }; |
| |
| static int container_fifo_init_entry(AVRefStructOpaque opaque, void *obj) |
| { |
| AVContainerFifo *cf = opaque.nc; |
| void **pobj = obj; |
| |
| *pobj = cf->container_alloc(cf->opaque); |
| if (!*pobj) |
| return AVERROR(ENOMEM); |
| |
| return 0; |
| } |
| |
| static void container_fifo_reset_entry(AVRefStructOpaque opaque, void *obj) |
| { |
| AVContainerFifo *cf = opaque.nc; |
| cf->container_reset(cf->opaque, *(void**)obj); |
| } |
| |
| static void container_fifo_free_entry(AVRefStructOpaque opaque, void *obj) |
| { |
| AVContainerFifo *cf = opaque.nc; |
| cf->container_free(cf->opaque, *(void**)obj); |
| } |
| |
| AVContainerFifo* |
| av_container_fifo_alloc(void *opaque, |
| void* (*container_alloc)(void *opaque), |
| void (*container_reset)(void *opaque, void *obj), |
| void (*container_free) (void *opaque, void *obj), |
| int (*fifo_transfer) (void *opaque, void *dst, void *src, unsigned flags), |
| unsigned flags) |
| { |
| AVContainerFifo *cf; |
| |
| cf = av_mallocz(sizeof(*cf)); |
| if (!cf) |
| return NULL; |
| |
| cf->opaque = opaque; |
| cf->container_alloc = container_alloc; |
| cf->container_reset = container_reset; |
| cf->container_free = container_free; |
| cf->fifo_transfer = fifo_transfer; |
| |
| cf->fifo = av_fifo_alloc2(1, sizeof(void*), AV_FIFO_FLAG_AUTO_GROW); |
| if (!cf->fifo) |
| goto fail; |
| |
| cf->pool = av_refstruct_pool_alloc_ext(sizeof(void*), 0, cf, |
| container_fifo_init_entry, |
| container_fifo_reset_entry, |
| container_fifo_free_entry, |
| NULL); |
| if (!cf->pool) |
| goto fail; |
| |
| return cf; |
| fail: |
| av_container_fifo_free(&cf); |
| return NULL; |
| } |
| |
| void av_container_fifo_free(AVContainerFifo **pcf) |
| { |
| AVContainerFifo *cf; |
| |
| if (!*pcf) |
| return; |
| |
| cf = *pcf; |
| |
| if (cf->fifo) { |
| void *obj; |
| while (av_fifo_read(cf->fifo, &obj, 1) >= 0) |
| av_refstruct_unref(&obj); |
| av_fifo_freep2(&cf->fifo); |
| } |
| |
| av_refstruct_pool_uninit(&cf->pool); |
| |
| av_freep(pcf); |
| } |
| |
| int av_container_fifo_read(AVContainerFifo *cf, void *obj, unsigned flags) |
| { |
| void **psrc; |
| int ret; |
| |
| ret = av_fifo_read(cf->fifo, &psrc, 1); |
| if (ret < 0) |
| return ret; |
| |
| ret = cf->fifo_transfer(cf->opaque, obj, *psrc, flags); |
| av_refstruct_unref(&psrc); |
| |
| return ret; |
| } |
| |
| int av_container_fifo_peek(AVContainerFifo *cf, void **pdst, size_t offset) |
| { |
| void **pobj; |
| int ret; |
| |
| ret = av_fifo_peek(cf->fifo, &pobj, 1, offset); |
| if (ret < 0) |
| return ret; |
| |
| *pdst = *pobj; |
| |
| return 0; |
| } |
| |
| void av_container_fifo_drain(AVContainerFifo *cf, size_t nb_elems) |
| { |
| av_assert0(nb_elems <= av_fifo_can_read(cf->fifo)); |
| while (nb_elems--) { |
| void **pobj; |
| int ret = av_fifo_read(cf->fifo, &pobj, 1); |
| av_assert0(ret >= 0); |
| av_refstruct_unref(&pobj); |
| } |
| } |
| |
| int av_container_fifo_write(AVContainerFifo *cf, void *obj, unsigned flags) |
| { |
| void **pdst; |
| int ret; |
| |
| pdst = av_refstruct_pool_get(cf->pool); |
| if (!pdst) |
| return AVERROR(ENOMEM); |
| |
| ret = cf->fifo_transfer(cf->opaque, *pdst, obj, flags); |
| if (ret < 0) |
| goto fail; |
| |
| ret = av_fifo_write(cf->fifo, &pdst, 1); |
| if (ret < 0) |
| goto fail; |
| |
| return 0; |
| fail: |
| av_refstruct_unref(&pdst); |
| return ret; |
| } |
| |
| size_t av_container_fifo_can_read(const AVContainerFifo *cf) |
| { |
| return av_fifo_can_read(cf->fifo); |
| } |
| |
| static void *frame_alloc(void *opaque) |
| { |
| return av_frame_alloc(); |
| } |
| |
| static void frame_reset(void *opaque, void *obj) |
| { |
| av_frame_unref(obj); |
| } |
| |
| static void frame_free(void *opaque, void *obj) |
| { |
| AVFrame *frame = obj; |
| av_frame_free(&frame); |
| } |
| |
| static int frame_transfer(void *opaque, void *dst, void *src, unsigned flags) |
| { |
| if (flags & AV_CONTAINER_FIFO_FLAG_REF) |
| return av_frame_ref(dst, src); |
| |
| av_frame_move_ref(dst, src); |
| return 0; |
| } |
| |
| AVContainerFifo *av_container_fifo_alloc_avframe(unsigned flags) |
| { |
| return av_container_fifo_alloc(NULL, frame_alloc, frame_reset, frame_free, |
| frame_transfer, 0); |
| } |