From 2e956d9c0f98a9b6fe1a0a3a858ab6d1fa4ec500 Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Thu, 17 Oct 2024 10:49:04 +0200 Subject: [PATCH] lavc/container_fifo: move to lavu and make public This can be useful in other places, e.g. it can replace objpool in fftools. The API is modified in the following nontrivial ways: * opaque pointers can be passed through to all user callbacks * read and write were previously separate callbacks in order to accomodate the caller wishing to write a new reference to the FIFO and keep the original one; the two callbacks are now merged into one, and a flags argument is added that allows to request such behaviour on a per-call basis * new peek and drain functions --- doc/APIchanges | 5 +- libavcodec/Makefile | 2 +- libavcodec/container_fifo.h | 89 -------------- libavcodec/hevc/hevcdec.c | 10 +- libavcodec/hevc/hevcdec.h | 2 +- libavcodec/hevc/refs.c | 4 +- libavutil/Makefile | 2 + {libavcodec => libavutil}/container_fifo.c | 116 ++++++++++-------- libavutil/container_fifo.h | 130 +++++++++++++++++++++ 9 files changed, 215 insertions(+), 145 deletions(-) delete mode 100644 libavcodec/container_fifo.h rename {libavcodec => libavutil}/container_fifo.c (51%) create mode 100644 libavutil/container_fifo.h diff --git a/doc/APIchanges b/doc/APIchanges index f01e5bf7bf..c3dab7fbcf 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -2,10 +2,13 @@ The last version increases of all libraries were on 2024-03-07 API changes, most recent first: -2024-12-15 - xxxxxxxxxx - lavu 59.51.100 - refstruct.h +2024-12-15 - xxxxxxxxxx - lavu 59.51.100 - refstruct.h container_fifo.h Add a new public header refstruct.h with new API for reference-counted objects. + Add a new public header container_fifo.h with new API for + a FIFO of container objects (e.g. AVFrame or AVPacket). + 2024-12-13 - xxxxxxxxxx - lavu 59.50.100 - channel_layout.h Add AV_CH_LAYOUT_9POINT1POINT6 and AV_CHANNEL_LAYOUT_9POINT1POINT6. diff --git a/libavcodec/Makefile b/libavcodec/Makefile index cd64013998..c946444175 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -442,7 +442,7 @@ OBJS-$(CONFIG_HCA_DECODER) += hcadec.o OBJS-$(CONFIG_HCOM_DECODER) += hcom.o OBJS-$(CONFIG_HDR_DECODER) += hdrdec.o OBJS-$(CONFIG_HDR_ENCODER) += hdrenc.o -OBJS-$(CONFIG_HEVC_DECODER) += aom_film_grain.o h274.o container_fifo.o +OBJS-$(CONFIG_HEVC_DECODER) += aom_film_grain.o h274.o OBJS-$(CONFIG_HEVC_AMF_ENCODER) += amfenc_hevc.o OBJS-$(CONFIG_HEVC_CUVID_DECODER) += cuviddec.o OBJS-$(CONFIG_HEVC_D3D12VA_ENCODER) += d3d12va_encode_hevc.o h265_profile_level.o \ diff --git a/libavcodec/container_fifo.h b/libavcodec/container_fifo.h deleted file mode 100644 index dd8b1d380f..0000000000 --- a/libavcodec/container_fifo.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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 - */ - -#ifndef AVCODEC_CONTAINER_FIFO_H -#define AVCODEC_CONTAINER_FIFO_H - -#include - -/** - * ContainerFifo is a FIFO for "containers" - dynamically allocated reusable - * structs (e.g. AVFrame or AVPacket). ContainerFifo uses an internal pool of - * such containers to avoid allocating and freeing them repeatedly. - */ -typedef struct ContainerFifo ContainerFifo; - -/** - * Allocate a new ContainerFifo for the container type defined by provided - * callbacks. - * - * @param container_alloc allocate a new container instance and return a pointer - * to it, or NULL on failure - * @param container_reset reset the provided container instance to a clean state - * @param container_free free the provided container instance - * @param fifo_write transfer the contents of src to dst, where src is a - * container instance provided to ff_container_fifo_write() - * @param fifo_read transfer the contents of src to dst in other cases - * - * @note fifo_read() and fifo_write() are different parameters in order to allow - * fifo_write() implementations that make a new reference in dst, leaving - * src untouched (see e.g. ff_container_fifo_alloc_avframe()) - */ -ContainerFifo* -ff_container_fifo_alloc(void* (*container_alloc)(void), - void (*container_reset)(void *obj), - void (*container_free) (void *obj), - int (*fifo_write) (void *dst, void *src), - int (*fifo_read) (void *dst, void *src)); - -/** - * Allocate a ContainerFifo instance for AVFrames. - * Note that ff_container_fifo_write() will call av_frame_ref() on src, making a - * new reference in dst and leaving src untouched. - * - * @param flags unused currently - */ -ContainerFifo *ff_container_fifo_alloc_avframe(unsigned flags); - -/** - * Free a ContainerFifo and everything in it. - */ -void ff_container_fifo_free(ContainerFifo **pf); - -/** - * Write the contents of obj to the FIFO. - * - * The fifo_write() callback previously provided to ff_container_fifo_alloc() - * will be called with obj as src in order to perform the actual transfer. - */ -int ff_container_fifo_write(ContainerFifo *pf, void *obj); - -/** - * Read the next available object from the FIFO into obj. - * - * The fifo_read() callback previously provided to ff_container_fifo_alloc() - * will be called with obj as dst in order to perform the actual transfer. - */ -int ff_container_fifo_read(ContainerFifo *pf, void *obj); - -/** - * @return number of objects available for reading - */ -size_t ff_container_fifo_can_read(ContainerFifo *pf); - -#endif // AVCODEC_CONTAINER_FIFO_H diff --git a/libavcodec/hevc/hevcdec.c b/libavcodec/hevc/hevcdec.c index 421579efe5..1a2f668053 100644 --- a/libavcodec/hevc/hevcdec.c +++ b/libavcodec/hevc/hevcdec.c @@ -28,6 +28,7 @@ #include "libavutil/attributes.h" #include "libavutil/avstring.h" #include "libavutil/common.h" +#include "libavutil/container_fifo.h" #include "libavutil/film_grain_params.h" #include "libavutil/internal.h" #include "libavutil/md5.h" @@ -41,7 +42,6 @@ #include "bswapdsp.h" #include "cabac_functions.h" #include "codec_internal.h" -#include "container_fifo.h" #include "decode.h" #include "golomb.h" #include "hevc.h" @@ -3748,7 +3748,7 @@ static int hevc_receive_frame(AVCodecContext *avctx, AVFrame *frame) s->pkt_dts = AV_NOPTS_VALUE; - if (ff_container_fifo_can_read(s->output_fifo)) + if (av_container_fifo_can_read(s->output_fifo)) goto do_output; av_packet_unref(avpkt); @@ -3786,7 +3786,7 @@ static int hevc_receive_frame(AVCodecContext *avctx, AVFrame *frame) return ret; do_output: - if (ff_container_fifo_read(s->output_fifo, frame) >= 0) { + if (av_container_fifo_read(s->output_fifo, frame, 0) >= 0) { if (!(avctx->export_side_data & AV_CODEC_EXPORT_DATA_FILM_GRAIN)) av_frame_remove_side_data(frame, AV_FRAME_DATA_FILM_GRAIN_PARAMS); @@ -3846,7 +3846,7 @@ static av_cold int hevc_decode_free(AVCodecContext *avctx) av_freep(&s->md5_ctx); - ff_container_fifo_free(&s->output_fifo); + av_container_fifo_free(&s->output_fifo); for (int layer = 0; layer < FF_ARRAY_ELEMS(s->layers); layer++) { HEVCLayerContext *l = &s->layers[layer]; @@ -3890,7 +3890,7 @@ static av_cold int hevc_init_context(AVCodecContext *avctx) s->local_ctx[0].logctx = avctx; s->local_ctx[0].common_cabac_state = &s->cabac; - s->output_fifo = ff_container_fifo_alloc_avframe(0); + s->output_fifo = av_container_fifo_alloc_avframe(0); if (!s->output_fifo) return AVERROR(ENOMEM); diff --git a/libavcodec/hevc/hevcdec.h b/libavcodec/hevc/hevcdec.h index b7a762089b..4e95035688 100644 --- a/libavcodec/hevc/hevcdec.h +++ b/libavcodec/hevc/hevcdec.h @@ -502,7 +502,7 @@ typedef struct HEVCContext { /** 1 if the independent slice segment header was successfully parsed */ uint8_t slice_initialized; - struct ContainerFifo *output_fifo; + struct AVContainerFifo *output_fifo; HEVCParamSets ps; HEVCSEI sei; diff --git a/libavcodec/hevc/refs.c b/libavcodec/hevc/refs.c index a467786f38..dd7f7f95a8 100644 --- a/libavcodec/hevc/refs.c +++ b/libavcodec/hevc/refs.c @@ -21,10 +21,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/container_fifo.h" #include "libavutil/mem.h" #include "libavutil/stereo3d.h" -#include "container_fifo.h" #include "decode.h" #include "hevc.h" #include "hevcdec.h" @@ -267,7 +267,7 @@ int ff_hevc_output_frames(HEVCContext *s, if (output) { f->pkt_dts = s->pkt_dts; - ret = ff_container_fifo_write(s->output_fifo, f); + ret = av_container_fifo_write(s->output_fifo, f, AV_CONTAINER_FIFO_FLAG_REF); } ff_hevc_unref_frame(frame, HEVC_FRAME_FLAG_OUTPUT); if (ret < 0) diff --git a/libavutil/Makefile b/libavutil/Makefile index ae1ad5199a..f8031815bd 100644 --- a/libavutil/Makefile +++ b/libavutil/Makefile @@ -19,6 +19,7 @@ HEADERS = adler32.h \ camellia.h \ channel_layout.h \ common.h \ + container_fifo.h \ cpu.h \ crc.h \ csp.h \ @@ -119,6 +120,7 @@ OBJS = adler32.o \ cast5.o \ camellia.o \ channel_layout.o \ + container_fifo.o \ cpu.o \ crc.o \ csp.o \ diff --git a/libavcodec/container_fifo.c b/libavutil/container_fifo.c similarity index 51% rename from libavcodec/container_fifo.c rename to libavutil/container_fifo.c index e1799e5eb7..34a78e7ef3 100644 --- a/libavcodec/container_fifo.c +++ b/libavutil/container_fifo.c @@ -16,32 +16,32 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/error.h" -#include "libavutil/fifo.h" -#include "libavutil/frame.h" -#include "libavutil/mem.h" - +#include "avassert.h" #include "container_fifo.h" -#include "libavutil/refstruct.h" +#include "error.h" +#include "fifo.h" +#include "frame.h" +#include "mem.h" +#include "refstruct.h" -struct ContainerFifo { +struct AVContainerFifo { AVFifo *fifo; AVRefStructPool *pool; - void* (*container_alloc)(void); - void (*container_reset)(void *obj); - void (*container_free) (void *obj); - int (*fifo_write) (void *dst, void *src); - int (*fifo_read) (void *dst, void *src); + 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) { - ContainerFifo *cf = opaque.nc; + AVContainerFifo *cf = opaque.nc; void **pobj = obj; - *pobj = cf->container_alloc(); + *pobj = cf->container_alloc(cf->opaque); if (!*pobj) return AVERROR(ENOMEM); @@ -50,34 +50,35 @@ static int container_fifo_init_entry(AVRefStructOpaque opaque, void *obj) static void container_fifo_reset_entry(AVRefStructOpaque opaque, void *obj) { - ContainerFifo *cf = opaque.nc; - cf->container_reset(*(void**)obj); + AVContainerFifo *cf = opaque.nc; + cf->container_reset(cf->opaque, *(void**)obj); } static void container_fifo_free_entry(AVRefStructOpaque opaque, void *obj) { - ContainerFifo *cf = opaque.nc; - cf->container_free(*(void**)obj); + AVContainerFifo *cf = opaque.nc; + cf->container_free(cf->opaque, *(void**)obj); } -ContainerFifo* -ff_container_fifo_alloc(void* (*container_alloc)(void), - void (*container_reset)(void *obj), - void (*container_free) (void *obj), - int (*fifo_write) (void *dst, void *src), - int (*fifo_read) (void *dst, void *src)) +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) { - ContainerFifo *cf; + 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_write = fifo_write; - cf->fifo_read = fifo_read; + cf->fifo_transfer = fifo_transfer; cf->fifo = av_fifo_alloc2(1, sizeof(void*), AV_FIFO_FLAG_AUTO_GROW); if (!cf->fifo) @@ -93,13 +94,13 @@ ff_container_fifo_alloc(void* (*container_alloc)(void), return cf; fail: - ff_container_fifo_free(&cf); + av_container_fifo_free(&cf); return NULL; } -void ff_container_fifo_free(ContainerFifo **pcf) +void av_container_fifo_free(AVContainerFifo **pcf) { - ContainerFifo *cf; + AVContainerFifo *cf; if (!*pcf) return; @@ -118,7 +119,7 @@ void ff_container_fifo_free(ContainerFifo **pcf) av_freep(pcf); } -int ff_container_fifo_read(ContainerFifo *cf, void *obj) +int av_container_fifo_read(AVContainerFifo *cf, void *obj, unsigned flags) { void **psrc; int ret; @@ -127,13 +128,38 @@ int ff_container_fifo_read(ContainerFifo *cf, void *obj) if (ret < 0) return ret; - ret = cf->fifo_read(obj, *psrc); + ret = cf->fifo_transfer(cf->opaque, obj, *psrc, flags); av_refstruct_unref(&psrc); return ret; } -int ff_container_fifo_write(ContainerFifo *cf, void *obj) +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; @@ -142,7 +168,7 @@ int ff_container_fifo_write(ContainerFifo *cf, void *obj) if (!pdst) return AVERROR(ENOMEM); - ret = cf->fifo_write(*pdst, obj); + ret = cf->fifo_transfer(cf->opaque, *pdst, obj, flags); if (ret < 0) goto fail; @@ -156,40 +182,38 @@ fail: return ret; } -size_t ff_container_fifo_can_read(ContainerFifo *cf) +size_t av_container_fifo_can_read(const AVContainerFifo *cf) { return av_fifo_can_read(cf->fifo); } -static void *frame_alloc(void) +static void *frame_alloc(void *opaque) { return av_frame_alloc(); } -static void frame_reset(void *obj) +static void frame_reset(void *opaque, void *obj) { av_frame_unref(obj); } -static void frame_free(void *obj) +static void frame_free(void *opaque, void *obj) { AVFrame *frame = obj; av_frame_free(&frame); } -static int frame_ref(void *dst, void *src) +static int frame_transfer(void *opaque, void *dst, void *src, unsigned flags) { - return av_frame_ref(dst, src); -} + if (flags & AV_CONTAINER_FIFO_FLAG_REF) + return av_frame_ref(dst, src); -static int frame_move_ref(void *dst, void *src) -{ av_frame_move_ref(dst, src); return 0; } -ContainerFifo *ff_container_fifo_alloc_avframe(unsigned flags) +AVContainerFifo *av_container_fifo_alloc_avframe(unsigned flags) { - return ff_container_fifo_alloc(frame_alloc, frame_reset, frame_free, - frame_ref, frame_move_ref); + return av_container_fifo_alloc(NULL, frame_alloc, frame_reset, frame_free, + frame_transfer, 0); } diff --git a/libavutil/container_fifo.h b/libavutil/container_fifo.h new file mode 100644 index 0000000000..ff9249311f --- /dev/null +++ b/libavutil/container_fifo.h @@ -0,0 +1,130 @@ +/* + * 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 + */ + +#ifndef AVUTIL_CONTAINER_FIFO_H +#define AVUTIL_CONTAINER_FIFO_H + +#include + +/** + * AVContainerFifo is a FIFO for "containers" - dynamically allocated reusable + * structs (e.g. AVFrame or AVPacket). AVContainerFifo uses an internal pool of + * such containers to avoid allocating and freeing them repeatedly. + */ +typedef struct AVContainerFifo AVContainerFifo; + +enum AVContainerFifoFlags { + /** + * Signal to av_container_fifo_write() that it should make a new reference + * to data in src rather than consume its contents. + * + * @note you must handle this flag manually in your own fifo_transfer() + * callback + */ + AV_CONTAINER_FIFO_FLAG_REF = (1 << 0), + + /** + * This and all higher bits in flags may be set to any value by the caller + * and are guaranteed to be passed through to the fifo_transfer() callback + * and not be interpreted by AVContainerFifo code. + */ + AV_CONTAINER_FIFO_FLAG_USER = (1 << 16), +}; + +/** + * Allocate a new AVContainerFifo for the container type defined by provided + * callbacks. + * + * @param opaque user data that will be passed to the callbacks provided to this + * function + * @param container_alloc allocate a new container instance and return a pointer + * to it, or NULL on failure + * @param container_reset reset the provided container instance to a clean state + * @param container_free free the provided container instance + * @param fifo_transfer Transfer the contents of container src to dst. + * @param flags currently unused + * + * @return newly allocated AVContainerFifo, or NULL on failure + */ +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); + +/** + * Allocate an AVContainerFifo instance for AVFrames. + * + * @param flags currently unused + */ +AVContainerFifo *av_container_fifo_alloc_avframe(unsigned flags); + +/** + * Free a AVContainerFifo and everything in it. + */ +void av_container_fifo_free(AVContainerFifo **cf); + +/** + * Write the contents of obj to the FIFO. + * + * The fifo_transfer() callback previously provided to av_container_fifo_alloc() + * will be called with obj as src in order to perform the actual transfer. + */ +int av_container_fifo_write(AVContainerFifo *cf, void *obj, unsigned flags); + +/** + * Read the next available object from the FIFO into obj. + * + * The fifo_read() callback previously provided to av_container_fifo_alloc() + * will be called with obj as dst in order to perform the actual transfer. + */ +int av_container_fifo_read(AVContainerFifo *cf, void *obj, unsigned flags); + +/** + * Access objects stored in the FIFO without retrieving them. The + * fifo_transfer() callback will NOT be invoked and the FIFO state will not be + * modified. + * + * @param pobj Pointer to the object stored in the FIFO will be written here on + * success. The object remains owned by the FIFO and the caller may + * only access it as long as the FIFO is not modified. + * @param offset Position of the object to retrieve - 0 is the next item that + * would be read, 1 the one after, etc. Must be smaller than + * av_container_fifo_can_read(). + * + * @retval 0 success, a pointer was written into pobj + * @retval AVERROR(EINVAL) invalid offset value + */ +int av_container_fifo_peek(AVContainerFifo *cf, void **pobj, size_t offset); + +/** + * Discard the specified number of elements from the FIFO. + * + * @param nb_elems number of elements to discard, MUST NOT be larger than + * av_fifo_can_read(f) + */ +void av_container_fifo_drain(AVContainerFifo *cf, size_t nb_elems); + +/** + * @return number of objects available for reading + */ +size_t av_container_fifo_can_read(const AVContainerFifo *cf); + +#endif // AVCODEC_CONTAINER_FIFO_H