From: Andreas Rheinhardt <andreas.rheinhardt@outlook.com> To: ffmpeg-devel@ffmpeg.org Cc: Andreas Rheinhardt <andreas.rheinhardt@outlook.com> Subject: [FFmpeg-devel] [PATCH 10/42] avcodec/cbs: Use RefStruct-API for unit content Date: Tue, 19 Sep 2023 21:57:02 +0200 Message-ID: <AS8P250MB0744A26920DF22C8524AF8258FFAA@AS8P250MB0744.EURP250.PROD.OUTLOOK.COM> (raw) In-Reply-To: <AS8P250MB074487CAD933FE4F6325C8EB8FFAA@AS8P250MB0744.EURP250.PROD.OUTLOOK.COM> This avoids allocations and error checks etc. as well as duplicate pointer lists in the CodedBitstreamFooContexts. It also avoids casting const away for use as opaque, as the RefStruct API supports const opaques. The fact that some of the units are not refcounted (i.e. they are sometimes part of an encoding context like VAAPIEncodeH264Context) meant that CodedBitstreamUnit still contains two pointers, one to the content and another ownership pointer, replacing the AVBufferRef* pointer. Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com> --- libavcodec/av1dec.c | 49 +++---------- libavcodec/av1dec.h | 10 +-- libavcodec/cbs.c | 101 ++++++++++++-------------- libavcodec/cbs.h | 12 +-- libavcodec/cbs_av1.c | 32 +++----- libavcodec/cbs_av1.h | 3 +- libavcodec/cbs_h264.h | 6 +- libavcodec/cbs_h2645.c | 94 ++++++++---------------- libavcodec/cbs_h265.h | 9 +-- libavcodec/cbs_h266.h | 11 +-- libavcodec/cbs_h266_syntax_template.c | 10 +-- libavcodec/cbs_internal.h | 7 +- 12 files changed, 129 insertions(+), 215 deletions(-) diff --git a/libavcodec/av1dec.c b/libavcodec/av1dec.c index 39ccad5bf6..87056520dd 100644 --- a/libavcodec/av1dec.c +++ b/libavcodec/av1dec.c @@ -37,6 +37,7 @@ #include "internal.h" #include "hwconfig.h" #include "profiles.h" +#include "refstruct.h" #include "thread.h" /**< same with Div_Lut defined in spec 7.11.3.7 */ @@ -641,7 +642,7 @@ static void av1_frame_unref(AVCodecContext *avctx, AV1Frame *f) ff_thread_release_buffer(avctx, f->f); av_buffer_unref(&f->hwaccel_priv_buf); f->hwaccel_picture_private = NULL; - av_buffer_unref(&f->header_ref); + ff_refstruct_unref(&f->header_ref); f->raw_frame_header = NULL; f->spatial_id = f->temporal_id = 0; memset(f->skip_mode_frame_idx, 0, @@ -654,9 +655,7 @@ static int av1_frame_ref(AVCodecContext *avctx, AV1Frame *dst, const AV1Frame *s { int ret; - ret = av_buffer_replace(&dst->header_ref, src->header_ref); - if (ret < 0) - return ret; + ff_refstruct_replace(&dst->header_ref, src->header_ref); dst->raw_frame_header = src->raw_frame_header; @@ -712,10 +711,10 @@ static av_cold int av1_decode_free(AVCodecContext *avctx) av1_frame_unref(avctx, &s->cur_frame); av_frame_free(&s->cur_frame.f); - av_buffer_unref(&s->seq_ref); - av_buffer_unref(&s->header_ref); - av_buffer_unref(&s->cll_ref); - av_buffer_unref(&s->mdcv_ref); + ff_refstruct_unref(&s->seq_ref); + ff_refstruct_unref(&s->header_ref); + ff_refstruct_unref(&s->cll_ref); + ff_refstruct_unref(&s->mdcv_ref); av_freep(&s->tile_group_info); while (s->itut_t35_fifo && av_fifo_read(s->itut_t35_fifo, &itut_t35, 1) >= 0) @@ -1160,9 +1159,7 @@ static int get_current_frame(AVCodecContext *avctx) av1_frame_unref(avctx, &s->cur_frame); - s->cur_frame.header_ref = av_buffer_ref(s->header_ref); - if (!s->cur_frame.header_ref) - return AVERROR(ENOMEM); + s->cur_frame.header_ref = ff_refstruct_ref(s->header_ref); s->cur_frame.raw_frame_header = s->raw_frame_header; @@ -1214,12 +1211,7 @@ static int av1_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame) switch (unit->type) { case AV1_OBU_SEQUENCE_HEADER: - av_buffer_unref(&s->seq_ref); - s->seq_ref = av_buffer_ref(unit->content_ref); - if (!s->seq_ref) { - ret = AVERROR(ENOMEM); - goto end; - } + ff_refstruct_replace(&s->seq_ref, unit->content_ref); s->raw_seq = &obu->obu.sequence_header; @@ -1264,12 +1256,7 @@ static int av1_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame) goto end; } - av_buffer_unref(&s->header_ref); - s->header_ref = av_buffer_ref(unit->content_ref); - if (!s->header_ref) { - ret = AVERROR(ENOMEM); - goto end; - } + ff_refstruct_replace(&s->header_ref, unit->content_ref); if (unit->type == AV1_OBU_FRAME) s->raw_frame_header = &obu->obu.frame.header; @@ -1356,23 +1343,11 @@ static int av1_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame) case AV1_OBU_METADATA: switch (obu->obu.metadata.metadata_type) { case AV1_METADATA_TYPE_HDR_CLL: - av_buffer_unref(&s->cll_ref); - s->cll_ref = av_buffer_ref(unit->content_ref); - if (!s->cll_ref) { - s->cll = NULL; - ret = AVERROR(ENOMEM); - goto end; - } + ff_refstruct_replace(&s->cll_ref, unit->content_ref); s->cll = &obu->obu.metadata.metadata.hdr_cll; break; case AV1_METADATA_TYPE_HDR_MDCV: - av_buffer_unref(&s->mdcv_ref); - s->mdcv_ref = av_buffer_ref(unit->content_ref); - if (!s->mdcv_ref) { - s->mdcv = NULL; - ret = AVERROR(ENOMEM); - goto end; - } + ff_refstruct_replace(&s->mdcv_ref, unit->content_ref); s->mdcv = &obu->obu.metadata.metadata.hdr_mdcv; break; case AV1_METADATA_TYPE_ITUT_T35: { diff --git a/libavcodec/av1dec.h b/libavcodec/av1dec.h index 59ffed1d9b..acbeec4af3 100644 --- a/libavcodec/av1dec.h +++ b/libavcodec/av1dec.h @@ -38,7 +38,7 @@ typedef struct AV1Frame { AVBufferRef *hwaccel_priv_buf; void *hwaccel_picture_private; - AVBufferRef *header_ref; + AV1RawOBU *header_ref; ///< RefStruct reference backing raw_frame_header. AV1RawFrameHeader *raw_frame_header; int temporal_id; @@ -71,15 +71,15 @@ typedef struct AV1DecContext { CodedBitstreamFragment current_obu; AVPacket *pkt; - AVBufferRef *seq_ref; + AV1RawOBU *seq_ref; ///< RefStruct reference backing raw_seq AV1RawSequenceHeader *raw_seq; - AVBufferRef *header_ref; + AV1RawOBU *header_ref; ///< RefStruct reference backing raw_frame_header AV1RawFrameHeader *raw_frame_header; TileGroupInfo *tile_group_info; - AVBufferRef *cll_ref; + AV1RawOBU *cll_ref; ///< RefStruct reference backing cll AV1RawMetadataHDRCLL *cll; - AVBufferRef *mdcv_ref; + AV1RawOBU *mdcv_ref; ///< RefStruct reference backing mdcv AV1RawMetadataHDRMDCV *mdcv; AVFifo *itut_t35_fifo; diff --git a/libavcodec/cbs.c b/libavcodec/cbs.c index 3ec8285e21..00c462b09d 100644 --- a/libavcodec/cbs.c +++ b/libavcodec/cbs.c @@ -28,6 +28,7 @@ #include "avcodec.h" #include "cbs.h" #include "cbs_internal.h" +#include "refstruct.h" static const CodedBitstreamType *const cbs_type_table[] = { @@ -151,7 +152,7 @@ av_cold void ff_cbs_close(CodedBitstreamContext **ctx_ptr) static void cbs_unit_uninit(CodedBitstreamUnit *unit) { - av_buffer_unref(&unit->content_ref); + ff_refstruct_unref(&unit->content_ref); unit->content = NULL; av_buffer_unref(&unit->data_ref); @@ -199,7 +200,7 @@ static int cbs_read_fragment_content(CodedBitstreamContext *ctx, continue; } - av_buffer_unref(&unit->content_ref); + ff_refstruct_unref(&unit->content_ref); unit->content = NULL; av_assert0(unit->data && unit->data_ref); @@ -213,7 +214,7 @@ static int cbs_read_fragment_content(CodedBitstreamContext *ctx, av_log(ctx->log_ctx, AV_LOG_VERBOSE, "Skipping decomposition of unit %d " "(type %"PRIu32").\n", i, unit->type); - av_buffer_unref(&unit->content_ref); + ff_refstruct_unref(&unit->content_ref); unit->content = NULL; } else if (err < 0) { av_log(ctx->log_ctx, AV_LOG_ERROR, "Failed to read unit %d " @@ -773,28 +774,22 @@ int ff_cbs_insert_unit_content(CodedBitstreamFragment *frag, int position, CodedBitstreamUnitType type, void *content, - AVBufferRef *content_buf) + void *content_ref) { CodedBitstreamUnit *unit; - AVBufferRef *content_ref; int err; if (position == -1) position = frag->nb_units; av_assert0(position >= 0 && position <= frag->nb_units); - if (content_buf) { - content_ref = av_buffer_ref(content_buf); - if (!content_ref) - return AVERROR(ENOMEM); - } else { - content_ref = NULL; - } - err = cbs_insert_unit(frag, position); - if (err < 0) { - av_buffer_unref(&content_ref); + if (err < 0) return err; + + if (content_ref) { + // Create our own reference out of the user-supplied one. + content_ref = ff_refstruct_ref(content_ref); } unit = &frag->units[position]; @@ -868,15 +863,14 @@ void ff_cbs_delete_unit(CodedBitstreamFragment *frag, (frag->nb_units - position) * sizeof(*frag->units)); } -static void cbs_default_free_unit_content(void *opaque, uint8_t *data) +static void cbs_default_free_unit_content(FFRefStructOpaque opaque, void *content) { - const CodedBitstreamUnitTypeDescriptor *desc = opaque; + const CodedBitstreamUnitTypeDescriptor *desc = opaque.c; for (int i = 0; i < desc->type.ref.nb_offsets; i++) { - void **ptr = (void**)(data + desc->type.ref.offsets[i]); + void **ptr = (void**)((char*)content + desc->type.ref.offsets[i]); av_buffer_unref((AVBufferRef**)(ptr + 1)); } - av_free(data); } static const CodedBitstreamUnitTypeDescriptor @@ -907,6 +901,15 @@ static const CodedBitstreamUnitTypeDescriptor return NULL; } +static void *cbs_alloc_content(const CodedBitstreamUnitTypeDescriptor *desc) +{ + return ff_refstruct_alloc_ext_c(desc->content_size, 0, + (FFRefStructOpaque){ .c = desc }, + desc->content_type == CBS_CONTENT_TYPE_COMPLEX + ? desc->type.complex.content_free + : cbs_default_free_unit_content); +} + int ff_cbs_alloc_unit_content(CodedBitstreamContext *ctx, CodedBitstreamUnit *unit) { @@ -918,27 +921,17 @@ int ff_cbs_alloc_unit_content(CodedBitstreamContext *ctx, if (!desc) return AVERROR(ENOSYS); - unit->content = av_mallocz(desc->content_size); - if (!unit->content) + unit->content_ref = cbs_alloc_content(desc); + if (!unit->content_ref) return AVERROR(ENOMEM); - - unit->content_ref = - av_buffer_create(unit->content, desc->content_size, - desc->content_type == CBS_CONTENT_TYPE_COMPLEX - ? desc->type.complex.content_free - : cbs_default_free_unit_content, - (void*)desc, 0); - if (!unit->content_ref) { - av_freep(&unit->content); - return AVERROR(ENOMEM); - } + unit->content = unit->content_ref; return 0; } -static int cbs_clone_internal_refs_unit_content(AVBufferRef **clone_ref, - const CodedBitstreamUnit *unit, - const CodedBitstreamUnitTypeDescriptor *desc) +static int cbs_clone_noncomplex_unit_content(void **clonep, + const CodedBitstreamUnit *unit, + const CodedBitstreamUnitTypeDescriptor *desc) { const uint8_t *src; uint8_t *copy; @@ -947,9 +940,15 @@ static int cbs_clone_internal_refs_unit_content(AVBufferRef **clone_ref, av_assert0(unit->content); src = unit->content; - copy = av_memdup(src, desc->content_size); + copy = cbs_alloc_content(desc); if (!copy) return AVERROR(ENOMEM); + memcpy(copy, src, desc->content_size); + for (int i = 0; i < desc->type.ref.nb_offsets; i++) { + void **ptr = (void**)(copy + desc->type.ref.offsets[i]); + /* Zero all the AVBufferRefs as they are owned by src. */ + *(ptr + 1) = NULL; + } for (i = 0; i < desc->type.ref.nb_offsets; i++) { const uint8_t *const *src_ptr = (const uint8_t* const*)(src + desc->type.ref.offsets[i]); @@ -975,22 +974,12 @@ static int cbs_clone_internal_refs_unit_content(AVBufferRef **clone_ref, goto fail; } } - - *clone_ref = av_buffer_create(copy, desc->content_size, - cbs_default_free_unit_content, - (void*)desc, 0); - if (!*clone_ref) { - err = AVERROR(ENOMEM); - goto fail; - } + *clonep = copy; return 0; fail: - for (--i; i >= 0; i--) - av_buffer_unref((AVBufferRef**)(copy + desc->type.ref.offsets[i])); - av_freep(©); - *clone_ref = NULL; + ff_refstruct_unref(©); return err; } @@ -1003,7 +992,7 @@ static int cbs_clone_unit_content(CodedBitstreamContext *ctx, CodedBitstreamUnit *unit) { const CodedBitstreamUnitTypeDescriptor *desc; - AVBufferRef *ref; + void *new_content; int err; desc = cbs_find_unit_type_desc(ctx, unit); @@ -1012,13 +1001,13 @@ static int cbs_clone_unit_content(CodedBitstreamContext *ctx, switch (desc->content_type) { case CBS_CONTENT_TYPE_INTERNAL_REFS: - err = cbs_clone_internal_refs_unit_content(&ref, unit, desc); + err = cbs_clone_noncomplex_unit_content(&new_content, unit, desc); break; case CBS_CONTENT_TYPE_COMPLEX: if (!desc->type.complex.content_clone) return AVERROR_PATCHWELCOME; - err = desc->type.complex.content_clone(&ref, unit); + err = desc->type.complex.content_clone(&new_content, unit); break; default: @@ -1028,8 +1017,8 @@ static int cbs_clone_unit_content(CodedBitstreamContext *ctx, if (err < 0) return err; - unit->content_ref = ref; - unit->content = ref->data; + unit->content_ref = new_content; + unit->content = new_content; return 0; } @@ -1045,17 +1034,17 @@ int ff_cbs_make_unit_refcounted(CodedBitstreamContext *ctx, int ff_cbs_make_unit_writable(CodedBitstreamContext *ctx, CodedBitstreamUnit *unit) { - AVBufferRef *ref = unit->content_ref; + void *ref = unit->content_ref; int err; av_assert0(unit->content); - if (ref && av_buffer_is_writable(ref)) + if (ref && ff_refstruct_exclusive(ref)) return 0; err = cbs_clone_unit_content(ctx, unit); if (err < 0) return err; - av_buffer_unref(&ref); + ff_refstruct_unref(&ref); return 0; } diff --git a/libavcodec/cbs.h b/libavcodec/cbs.h index b4131db5fe..68f7e4b0a9 100644 --- a/libavcodec/cbs.h +++ b/libavcodec/cbs.h @@ -106,10 +106,10 @@ typedef struct CodedBitstreamUnit { */ void *content; /** - * If content is reference counted, a reference to the buffer containing - * content. Null if content is not reference counted. + * If content is reference counted, a RefStruct reference backing content. + * NULL if content is not reference counted. */ - AVBufferRef *content_ref; + void *content_ref; } CodedBitstreamUnit; /** @@ -375,14 +375,16 @@ int ff_cbs_alloc_unit_content(CodedBitstreamContext *ctx, /** * Insert a new unit into a fragment with the given content. * + * If content_ref is supplied, it has to be a RefStruct reference + * backing content; the user keeps ownership of the supplied reference. * The content structure continues to be owned by the caller if - * content_buf is not supplied. + * content_ref is not supplied. */ int ff_cbs_insert_unit_content(CodedBitstreamFragment *frag, int position, CodedBitstreamUnitType type, void *content, - AVBufferRef *content_buf); + void *content_ref); /** * Add a new unit to a fragment with the given data bitstream. diff --git a/libavcodec/cbs_av1.c b/libavcodec/cbs_av1.c index 7fb5bd8b42..50a5c3e117 100644 --- a/libavcodec/cbs_av1.c +++ b/libavcodec/cbs_av1.c @@ -24,6 +24,7 @@ #include "cbs_internal.h" #include "cbs_av1.h" #include "defs.h" +#include "refstruct.h" static int cbs_av1_read_uvlc(CodedBitstreamContext *ctx, GetBitContext *gbc, @@ -953,12 +954,7 @@ static int cbs_av1_read_unit(CodedBitstreamContext *ctx, priv->operating_point_idc = sequence_header->operating_point_idc[priv->operating_point]; } - av_buffer_unref(&priv->sequence_header_ref); - priv->sequence_header = NULL; - - priv->sequence_header_ref = av_buffer_ref(unit->content_ref); - if (!priv->sequence_header_ref) - return AVERROR(ENOMEM); + ff_refstruct_replace(&priv->sequence_header_ref, unit->content_ref); priv->sequence_header = &obu->obu.sequence_header; } break; @@ -1077,9 +1073,7 @@ static int cbs_av1_write_obu(CodedBitstreamContext *ctx, av1ctx = *priv; if (priv->sequence_header_ref) { - av1ctx.sequence_header_ref = av_buffer_ref(priv->sequence_header_ref); - if (!av1ctx.sequence_header_ref) - return AVERROR(ENOMEM); + av1ctx.sequence_header_ref = ff_refstruct_ref(priv->sequence_header_ref); } if (priv->frame_header_ref) { @@ -1112,19 +1106,14 @@ static int cbs_av1_write_obu(CodedBitstreamContext *ctx, if (err < 0) goto error; - av_buffer_unref(&priv->sequence_header_ref); + ff_refstruct_unref(&priv->sequence_header_ref); priv->sequence_header = NULL; err = ff_cbs_make_unit_refcounted(ctx, unit); if (err < 0) goto error; - priv->sequence_header_ref = av_buffer_ref(unit->content_ref); - if (!priv->sequence_header_ref) { - err = AVERROR(ENOMEM); - goto error; - } - + priv->sequence_header_ref = ff_refstruct_ref(unit->content_ref); priv->sequence_header = &obu->obu.sequence_header; } break; @@ -1227,7 +1216,7 @@ static int cbs_av1_write_obu(CodedBitstreamContext *ctx, av_assert0(data_pos <= start_pos); if (8 * obu->obu_size > put_bits_left(pbc)) { - av_buffer_unref(&priv->sequence_header_ref); + ff_refstruct_unref(&priv->sequence_header_ref); av_buffer_unref(&priv->frame_header_ref); *priv = av1ctx; @@ -1251,7 +1240,7 @@ static int cbs_av1_write_obu(CodedBitstreamContext *ctx, err = 0; error: - av_buffer_unref(&av1ctx.sequence_header_ref); + ff_refstruct_unref(&av1ctx.sequence_header_ref); av_buffer_unref(&av1ctx.frame_header_ref); return err; @@ -1303,13 +1292,13 @@ static void cbs_av1_close(CodedBitstreamContext *ctx) { CodedBitstreamAV1Context *priv = ctx->priv_data; - av_buffer_unref(&priv->sequence_header_ref); + ff_refstruct_unref(&priv->sequence_header_ref); av_buffer_unref(&priv->frame_header_ref); } -static void cbs_av1_free_metadata(void *unit, uint8_t *content) +static void cbs_av1_free_metadata(FFRefStructOpaque unused, void *content) { - AV1RawOBU *obu = (AV1RawOBU*)content; + AV1RawOBU *obu = content; AV1RawMetadata *md; av_assert0(obu->header.obu_type == AV1_OBU_METADATA); @@ -1327,7 +1316,6 @@ static void cbs_av1_free_metadata(void *unit, uint8_t *content) default: av_buffer_unref(&md->metadata.unknown.payload_ref); } - av_free(content); } static const CodedBitstreamUnitTypeDescriptor cbs_av1_unit_types[] = { diff --git a/libavcodec/cbs_av1.h b/libavcodec/cbs_av1.h index 64dfdce9c4..63917f2f16 100644 --- a/libavcodec/cbs_av1.h +++ b/libavcodec/cbs_av1.h @@ -437,7 +437,8 @@ typedef struct CodedBitstreamAV1Context { const AVClass *class; AV1RawSequenceHeader *sequence_header; - AVBufferRef *sequence_header_ref; + /** A RefStruct reference backing sequence_header. */ + AV1RawOBU *sequence_header_ref; int seen_frame_header; AVBufferRef *frame_header_ref; diff --git a/libavcodec/cbs_h264.h b/libavcodec/cbs_h264.h index ca9b688c05..db91231337 100644 --- a/libavcodec/cbs_h264.h +++ b/libavcodec/cbs_h264.h @@ -407,10 +407,8 @@ typedef struct CodedBitstreamH264Context { // All currently available parameter sets. These are updated when // any parameter set NAL unit is read/written with this context. - AVBufferRef *sps_ref[H264_MAX_SPS_COUNT]; - AVBufferRef *pps_ref[H264_MAX_PPS_COUNT]; - H264RawSPS *sps[H264_MAX_SPS_COUNT]; - H264RawPPS *pps[H264_MAX_PPS_COUNT]; + H264RawSPS *sps[H264_MAX_SPS_COUNT]; ///< RefStruct references + H264RawPPS *pps[H264_MAX_PPS_COUNT]; ///< RefStruct references // The currently active parameter sets. These are updated when any // NAL unit refers to the relevant parameter set. These pointers diff --git a/libavcodec/cbs_h2645.c b/libavcodec/cbs_h2645.c index 318c997d94..e071442c31 100644 --- a/libavcodec/cbs_h2645.c +++ b/libavcodec/cbs_h2645.c @@ -28,6 +28,7 @@ #include "h264.h" #include "h2645_parse.h" #include "hevc.h" +#include "refstruct.h" #include "vvc.h" @@ -759,12 +760,8 @@ static int cbs_h26 ## h26n ## _replace_ ## ps_var(CodedBitstreamContext *ctx, \ return err; \ if (priv->ps_var[id] == priv->active_ ## ps_var) \ priv->active_ ## ps_var = NULL ; \ - av_buffer_unref(&priv->ps_var ## _ref[id]); \ av_assert0(unit->content_ref); \ - priv->ps_var ## _ref[id] = av_buffer_ref(unit->content_ref); \ - if (!priv->ps_var ## _ref[id]) \ - return AVERROR(ENOMEM); \ - priv->ps_var[id] = (H26 ## h26n ## Raw ## ps_name *)priv->ps_var ## _ref[id]->data; \ + ff_refstruct_replace(&priv->ps_var[id], unit->content_ref); \ return 0; \ } @@ -784,12 +781,8 @@ static int cbs_h26 ## h26n ## _replace_ ## ps_var(CodedBitstreamContext *ctx, \ int err = ff_cbs_make_unit_refcounted(ctx, unit); \ if (err < 0) \ return err; \ - av_buffer_unref(&priv->ps_var ## _ref[id]); \ av_assert0(unit->content_ref); \ - priv->ps_var ## _ref[id] = av_buffer_ref(unit->content_ref); \ - if (!priv->ps_var ## _ref[id]) \ - return AVERROR(ENOMEM); \ - priv->ps_var[id] = (H26 ## h26n ## Raw ## ps_name *)priv->ps_var ## _ref[id]->data; \ + ff_refstruct_replace(&priv->ps_var[id], unit->content_ref); \ return 0; \ } @@ -808,9 +801,7 @@ static int cbs_h266_replace_ph(CodedBitstreamContext *ctx, if (err < 0) return err; av_assert0(unit->content_ref); - err = av_buffer_replace(&h266->ph_ref, unit->content_ref); - if (err < 0) - return err; + ff_refstruct_replace(&h266->ph_ref, unit->content_ref); h266->ph = ph; return 0; } @@ -1874,14 +1865,10 @@ static void cbs_h264_flush(CodedBitstreamContext *ctx) { CodedBitstreamH264Context *h264 = ctx->priv_data; - for (int i = 0; i < FF_ARRAY_ELEMS(h264->sps); i++) { - av_buffer_unref(&h264->sps_ref[i]); - h264->sps[i] = NULL; - } - for (int i = 0; i < FF_ARRAY_ELEMS(h264->pps); i++) { - av_buffer_unref(&h264->pps_ref[i]); - h264->pps[i] = NULL; - } + for (int i = 0; i < FF_ARRAY_ELEMS(h264->sps); i++) + ff_refstruct_unref(&h264->sps[i]); + for (int i = 0; i < FF_ARRAY_ELEMS(h264->pps); i++) + ff_refstruct_unref(&h264->pps[i]); h264->active_sps = NULL; h264->active_pps = NULL; @@ -1896,27 +1883,21 @@ static void cbs_h264_close(CodedBitstreamContext *ctx) ff_h2645_packet_uninit(&h264->common.read_packet); for (i = 0; i < FF_ARRAY_ELEMS(h264->sps); i++) - av_buffer_unref(&h264->sps_ref[i]); + ff_refstruct_unref(&h264->sps[i]); for (i = 0; i < FF_ARRAY_ELEMS(h264->pps); i++) - av_buffer_unref(&h264->pps_ref[i]); + ff_refstruct_unref(&h264->pps[i]); } static void cbs_h265_flush(CodedBitstreamContext *ctx) { CodedBitstreamH265Context *h265 = ctx->priv_data; - for (int i = 0; i < FF_ARRAY_ELEMS(h265->vps); i++) { - av_buffer_unref(&h265->vps_ref[i]); - h265->vps[i] = NULL; - } - for (int i = 0; i < FF_ARRAY_ELEMS(h265->sps); i++) { - av_buffer_unref(&h265->sps_ref[i]); - h265->sps[i] = NULL; - } - for (int i = 0; i < FF_ARRAY_ELEMS(h265->pps); i++) { - av_buffer_unref(&h265->pps_ref[i]); - h265->pps[i] = NULL; - } + for (int i = 0; i < FF_ARRAY_ELEMS(h265->vps); i++) + ff_refstruct_unref(&h265->vps[i]); + for (int i = 0; i < FF_ARRAY_ELEMS(h265->sps); i++) + ff_refstruct_unref(&h265->sps[i]); + for (int i = 0; i < FF_ARRAY_ELEMS(h265->pps); i++) + ff_refstruct_unref(&h265->pps[i]); h265->active_vps = NULL; h265->active_sps = NULL; @@ -1931,32 +1912,24 @@ static void cbs_h265_close(CodedBitstreamContext *ctx) ff_h2645_packet_uninit(&h265->common.read_packet); for (i = 0; i < FF_ARRAY_ELEMS(h265->vps); i++) - av_buffer_unref(&h265->vps_ref[i]); + ff_refstruct_unref(&h265->vps[i]); for (i = 0; i < FF_ARRAY_ELEMS(h265->sps); i++) - av_buffer_unref(&h265->sps_ref[i]); + ff_refstruct_unref(&h265->sps[i]); for (i = 0; i < FF_ARRAY_ELEMS(h265->pps); i++) - av_buffer_unref(&h265->pps_ref[i]); + ff_refstruct_unref(&h265->pps[i]); } static void cbs_h266_flush(CodedBitstreamContext *ctx) { CodedBitstreamH266Context *h266 = ctx->priv_data; - for (int i = 0; i < FF_ARRAY_ELEMS(h266->vps); i++) { - av_buffer_unref(&h266->vps_ref[i]); - h266->vps[i] = NULL; - } - - for (int i = 0; i < FF_ARRAY_ELEMS(h266->sps); i++) { - av_buffer_unref(&h266->sps_ref[i]); - h266->sps[i] = NULL; - } - for (int i = 0; i < FF_ARRAY_ELEMS(h266->pps); i++) { - av_buffer_unref(&h266->pps_ref[i]); - h266->pps[i] = NULL; - } - av_buffer_unref(&h266->ph_ref); - h266->ph = NULL; + for (int i = 0; i < FF_ARRAY_ELEMS(h266->vps); i++) + ff_refstruct_unref(&h266->vps[i]); + for (int i = 0; i < FF_ARRAY_ELEMS(h266->sps); i++) + ff_refstruct_unref(&h266->sps[i]); + for (int i = 0; i < FF_ARRAY_ELEMS(h266->pps); i++) + ff_refstruct_unref(&h266->pps[i]); + ff_refstruct_unref(&h266->ph_ref); } static void cbs_h266_close(CodedBitstreamContext *ctx) @@ -1967,11 +1940,10 @@ static void cbs_h266_close(CodedBitstreamContext *ctx) ff_h2645_packet_uninit(&h266->common.read_packet); } -static void cbs_h264_free_sei(void *opaque, uint8_t *content) +static void cbs_h264_free_sei(FFRefStructOpaque unused, void *content) { - H264RawSEI *sei = (H264RawSEI*)content; + H264RawSEI *sei = content; ff_cbs_sei_free_message_list(&sei->message_list); - av_free(content); } static const CodedBitstreamUnitTypeDescriptor cbs_h264_unit_types[] = { @@ -1994,11 +1966,10 @@ static const CodedBitstreamUnitTypeDescriptor cbs_h264_unit_types[] = { CBS_UNIT_TYPE_END_OF_LIST }; -static void cbs_h265_free_sei(void *opaque, uint8_t *content) +static void cbs_h265_free_sei(FFRefStructOpaque unused, void *content) { - H265RawSEI *sei = (H265RawSEI*)content; + H265RawSEI *sei = content; ff_cbs_sei_free_message_list(&sei->message_list); - av_free(content); } static const CodedBitstreamUnitTypeDescriptor cbs_h265_unit_types[] = { @@ -2021,11 +1992,10 @@ static const CodedBitstreamUnitTypeDescriptor cbs_h265_unit_types[] = { CBS_UNIT_TYPE_END_OF_LIST }; -static void cbs_h266_free_sei(void *opaque, uint8_t *content) +static void cbs_h266_free_sei(FFRefStructOpaque unused, void *content) { - H266RawSEI *sei = (H266RawSEI*)content; + H266RawSEI *sei = content; ff_cbs_sei_free_message_list(&sei->message_list); - av_free(content); } static const CodedBitstreamUnitTypeDescriptor cbs_h266_unit_types[] = { diff --git a/libavcodec/cbs_h265.h b/libavcodec/cbs_h265.h index f7cbd4970d..1b1195f198 100644 --- a/libavcodec/cbs_h265.h +++ b/libavcodec/cbs_h265.h @@ -681,12 +681,9 @@ typedef struct CodedBitstreamH265Context { // All currently available parameter sets. These are updated when // any parameter set NAL unit is read/written with this context. - AVBufferRef *vps_ref[HEVC_MAX_VPS_COUNT]; - AVBufferRef *sps_ref[HEVC_MAX_SPS_COUNT]; - AVBufferRef *pps_ref[HEVC_MAX_PPS_COUNT]; - H265RawVPS *vps[HEVC_MAX_VPS_COUNT]; - H265RawSPS *sps[HEVC_MAX_SPS_COUNT]; - H265RawPPS *pps[HEVC_MAX_PPS_COUNT]; + H265RawVPS *vps[HEVC_MAX_VPS_COUNT]; ///< RefStruct references + H265RawSPS *sps[HEVC_MAX_SPS_COUNT]; ///< RefStruct references + H265RawPPS *pps[HEVC_MAX_PPS_COUNT]; ///< RefStruct references // The currently active parameter sets. These are updated when any // NAL unit refers to the relevant parameter set. These pointers diff --git a/libavcodec/cbs_h266.h b/libavcodec/cbs_h266.h index 3a6f6d96b5..d2ba99f522 100644 --- a/libavcodec/cbs_h266.h +++ b/libavcodec/cbs_h266.h @@ -867,14 +867,11 @@ typedef struct CodedBitstreamH266Context { // All currently available parameter sets. These are updated when // any parameter set NAL unit is read/written with this context. - AVBufferRef *vps_ref[VVC_MAX_VPS_COUNT]; - AVBufferRef *sps_ref[VVC_MAX_SPS_COUNT]; - AVBufferRef *pps_ref[VVC_MAX_PPS_COUNT]; - AVBufferRef *ph_ref; - H266RawVPS *vps[VVC_MAX_VPS_COUNT]; - H266RawSPS *sps[VVC_MAX_SPS_COUNT]; - H266RawPPS *pps[VVC_MAX_PPS_COUNT]; + H266RawVPS *vps[VVC_MAX_VPS_COUNT]; ///< RefStruct references + H266RawSPS *sps[VVC_MAX_SPS_COUNT]; ///< RefStruct references + H266RawPPS *pps[VVC_MAX_PPS_COUNT]; ///< RefStruct references H266RawPictureHeader *ph; + void *ph_ref; ///< RefStruct reference backing ph above } CodedBitstreamH266Context; #endif /* AVCODEC_CBS_H266_H */ diff --git a/libavcodec/cbs_h266_syntax_template.c b/libavcodec/cbs_h266_syntax_template.c index 4075897b9a..a0f48a86c3 100644 --- a/libavcodec/cbs_h266_syntax_template.c +++ b/libavcodec/cbs_h266_syntax_template.c @@ -1072,17 +1072,13 @@ static int FUNC(sps)(CodedBitstreamContext *ctx, RWContext *rw, ub(4, sps_seq_parameter_set_id); ub(4, sps_video_parameter_set_id); - if (current->sps_video_parameter_set_id == 0 && !h266->vps_ref[0]) { - H266RawVPS *vps; - AVBufferRef *ref = av_buffer_allocz(sizeof(H266RawVPS)); - if (!ref) { + if (current->sps_video_parameter_set_id == 0 && !h266->vps[0]) { + H266RawVPS *vps = ff_refstruct_allocz(sizeof(*vps)); + if (!vps) return AVERROR(ENOMEM); - } - vps = (H266RawVPS *) ref->data; vps->vps_max_layers_minus1 = 0; vps->vps_independent_layer_flag[0] = 1; vps->vps_layer_id[0] = current->nal_unit_header.nuh_layer_id; - h266->vps_ref[0] = ref; h266->vps[0] = vps; } diff --git a/libavcodec/cbs_internal.h b/libavcodec/cbs_internal.h index da84697a29..10a95d7d6e 100644 --- a/libavcodec/cbs_internal.h +++ b/libavcodec/cbs_internal.h @@ -19,15 +19,16 @@ #ifndef AVCODEC_CBS_INTERNAL_H #define AVCODEC_CBS_INTERNAL_H +#include <stddef.h> #include <stdint.h> -#include "libavutil/buffer.h" #include "libavutil/log.h" #include "cbs.h" #include "codec_id.h" #include "get_bits.h" #include "put_bits.h" +#include "refstruct.h" enum CBSContentType { @@ -92,8 +93,8 @@ typedef const struct CodedBitstreamUnitTypeDescriptor { } ref; struct { - void (*content_free)(void *opaque, uint8_t *data); - int (*content_clone)(AVBufferRef **ref, CodedBitstreamUnit *unit); + void (*content_free)(FFRefStructOpaque opaque, void *content); + int (*content_clone)(void **new_content, CodedBitstreamUnit *unit); } complex; } type; } CodedBitstreamUnitTypeDescriptor; -- 2.34.1 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
next prev parent reply other threads:[~2023-09-19 19:58 UTC|newest] Thread overview: 106+ messages / expand[flat|nested] mbox.gz Atom feed top 2023-09-19 19:38 [FFmpeg-devel] [PATCH 00/42] New API for reference counting and ThreadFrames Andreas Rheinhardt 2023-09-19 19:56 ` [FFmpeg-devel] [PATCH 01/42] tests/fate-run: Ensure that THREADS=random is actually random Andreas Rheinhardt 2023-09-25 20:01 ` Andreas Rheinhardt 2023-09-19 19:56 ` [FFmpeg-devel] [PATCH 02/42] avcodec/refstruct: Add simple API for refcounted objects Andreas Rheinhardt 2023-09-21 19:58 ` Nicolas George 2023-09-21 23:07 ` Andreas Rheinhardt 2023-10-06 18:24 ` Andreas Rheinhardt 2023-10-06 19:43 ` Nicolas George 2023-10-06 20:20 ` Andreas Rheinhardt 2023-10-06 20:37 ` Nicolas George 2023-10-06 20:50 ` Andreas Rheinhardt 2023-10-06 21:22 ` Nicolas George 2023-10-07 21:03 ` James Almer 2023-09-19 19:56 ` [FFmpeg-devel] [PATCH 03/42] avcodec/get_buffer: Use RefStruct API for FramePool Andreas Rheinhardt 2023-09-28 12:36 ` Anton Khirnov 2023-09-19 19:56 ` [FFmpeg-devel] [PATCH 04/42] avcodec/h264_ps: Use RefStruct API for SPS/PPS Andreas Rheinhardt 2023-09-28 13:03 ` Anton Khirnov 2023-09-28 15:49 ` Andreas Rheinhardt 2023-10-02 9:39 ` Anton Khirnov 2023-09-19 19:56 ` [FFmpeg-devel] [PATCH 05/42] avcodec/hevc_ps: Use RefStruct API for parameter sets Andreas Rheinhardt 2023-09-28 13:13 ` Anton Khirnov 2023-09-19 19:56 ` [FFmpeg-devel] [PATCH 06/42] avcodec/vp8: Use RefStruct API for seg_map Andreas Rheinhardt 2023-10-02 9:44 ` Anton Khirnov 2023-10-02 10:04 ` Andreas Rheinhardt 2023-10-02 10:14 ` Anton Khirnov 2023-09-19 19:56 ` [FFmpeg-devel] [PATCH 07/42] avcodec/wavpack: Use RefStruct API for DSD context Andreas Rheinhardt 2023-10-02 9:46 ` Anton Khirnov 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 08/42] avcodec/dovi_rpu: Use RefStruct API for Vdr data Andreas Rheinhardt 2023-10-02 9:51 ` Anton Khirnov 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 09/42] avcodec/refstruct: Allow checking for exclusive ownership Andreas Rheinhardt 2023-09-19 19:57 ` Andreas Rheinhardt [this message] 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 11/42] avcodec/cbs_sei: Use RefStruct API for SEI messages Andreas Rheinhardt 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 12/42] avcodec/decode: Use RefStruct API for hwaccel_picture_private Andreas Rheinhardt 2023-10-02 10:39 ` Anton Khirnov 2023-10-02 12:30 ` Lynne 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 13/42] avcodec/vulkan_decode: Use RefStruct API for shared_ref Andreas Rheinhardt 2023-10-02 12:31 ` Lynne 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 14/42] avcodec/hevcdec: Use RefStruct API for RefPicListTap buffer Andreas Rheinhardt 2023-10-02 10:47 ` Anton Khirnov 2023-10-02 11:07 ` Andreas Rheinhardt 2023-10-04 8:10 ` Anton Khirnov 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 15/42] avcodec/pthread_frame: Use RefStruct API for ThreadFrame.progress Andreas Rheinhardt 2023-10-02 11:01 ` Anton Khirnov 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 16/42] avcodec/nvdec: Use RefStruct API for decoder_ref Andreas Rheinhardt 2023-10-02 10:58 ` Anton Khirnov 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 17/42] avcodec/refstruct: Add RefStruct pool API Andreas Rheinhardt 2023-09-20 19:58 ` Michael Niedermayer 2023-09-21 0:28 ` Andreas Rheinhardt 2023-10-04 8:39 ` Anton Khirnov 2023-10-04 11:09 ` Andreas Rheinhardt 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 18/42] avcodec/h264dec: Use RefStruct-pool API instead of AVBufferPool API Andreas Rheinhardt 2023-10-04 14:07 ` Anton Khirnov 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 19/42] avcodec/hevcdec: " Andreas Rheinhardt 2023-10-04 14:12 ` Anton Khirnov 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 20/42] avcodec/nvdec: Use RefStruct-pool API for decoder pool Andreas Rheinhardt 2023-10-04 14:28 ` Anton Khirnov 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 21/42] avcodec/refstruct: Allow to always return zeroed pool entries Andreas Rheinhardt 2023-10-12 12:45 ` Anton Khirnov 2023-10-12 13:25 ` Andreas Rheinhardt 2023-10-12 13:56 ` Anton Khirnov 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 22/42] avcodec/vp9: Use RefStruct-pool API for extradata Andreas Rheinhardt 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 23/42] avcodec/vaapi_encode: Use RefStruct pool API, stop abusing AVBuffer API Andreas Rheinhardt 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 24/42] avcodec/refstruct: Allow to share pools Andreas Rheinhardt 2023-10-12 13:04 ` Anton Khirnov 2023-10-12 13:51 ` Andreas Rheinhardt 2023-10-12 14:04 ` Anton Khirnov 2023-10-12 14:10 ` Andreas Rheinhardt 2023-10-12 17:09 ` Andreas Rheinhardt 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 25/42] avcodec/vp9: Join extradata buffer pools Andreas Rheinhardt 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 26/42] avcodec/refstruct: Allow to use a dynamic opaque Andreas Rheinhardt 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 27/42] avcodec/pthread_frame: Add new progress API Andreas Rheinhardt 2023-10-21 10:34 ` Anton Khirnov 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 28/42] avcodec/mimic: Switch to ProgressFrames Andreas Rheinhardt 2023-10-21 10:38 ` Anton Khirnov 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 29/42] avcodec/vp3: " Andreas Rheinhardt 2023-10-21 10:48 ` Anton Khirnov 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 30/42] avcodec/vp9: " Andreas Rheinhardt 2023-10-21 11:04 ` Anton Khirnov 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 31/42] avcodec/vp9: Fix race when attaching side-data for show-existing frame Andreas Rheinhardt 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 32/42] avcodec/vp9: Reduce wait times Andreas Rheinhardt 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 33/42] avcodec/vp9: Simplify replacing VP9Frame Andreas Rheinhardt 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 34/42] avcodec/vp9: Replace atomic_store() by atomic_init() Andreas Rheinhardt 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 35/42] avcodec/threadprogress: Add new API for frame-threaded progress Andreas Rheinhardt 2023-09-20 19:44 ` Michael Niedermayer 2023-09-21 0:28 ` Andreas Rheinhardt 2023-10-25 13:25 ` Anton Khirnov 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 36/42] avcodec/wavpack: Use ThreadProgress API Andreas Rheinhardt 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 37/42] avcodec/vp8: Convert to ProgressFrame API Andreas Rheinhardt 2023-10-25 13:35 ` Anton Khirnov 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 38/42] avcodec/codec_internal: Remove FF_CODEC_CAP_ALLOCATE_PROGRESS Andreas Rheinhardt 2023-10-25 13:38 ` Anton Khirnov 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 39/42] avcodec/hevcdec: Move collocated_ref to HEVCContext Andreas Rheinhardt 2023-10-25 13:42 ` Anton Khirnov 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 40/42] avcodec/hevcdec: Switch to ProgressFrames Andreas Rheinhardt 2023-11-09 9:50 ` Anton Khirnov 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 41/42] avcodec/pngdec: " Andreas Rheinhardt 2023-11-09 9:52 ` Anton Khirnov 2023-09-19 19:57 ` [FFmpeg-devel] [PATCH 42/42] avcodec/ffv1dec: " Andreas Rheinhardt 2023-11-09 9:56 ` Anton Khirnov 2023-10-02 18:13 ` [FFmpeg-devel] [PATCH 43/49] avcodec/qsv: Use RefStruct API for memory id (mids) array Andreas Rheinhardt 2023-10-02 18:13 ` [FFmpeg-devel] [PATCH 44/49] avcodec/rkmppdec: Fix double-free on error Andreas Rheinhardt 2023-10-02 18:13 ` [FFmpeg-devel] [PATCH 45/49] avcodec/rkmppdec: Check av_buffer_ref() Andreas Rheinhardt 2023-10-02 18:13 ` [FFmpeg-devel] [PATCH 46/49] avcodec/rkmppdec: Use RefStruct API for references to decoder itself Andreas Rheinhardt 2023-10-02 18:13 ` [FFmpeg-devel] [PATCH 47/49] avcodec/rkmppdec: Allocate AVDRMFrameDescriptor and frame ctx jointly Andreas Rheinhardt 2023-10-02 18:13 ` [FFmpeg-devel] [PATCH 48/49] avcodec/v4l2_m2m: Remove redundant av_frame_unref() Andreas Rheinhardt 2023-10-02 18:13 ` [FFmpeg-devel] [PATCH 49/49] avcodec/v4l2_(m2m|buffers): Use RefStruct API for context references Andreas Rheinhardt
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=AS8P250MB0744A26920DF22C8524AF8258FFAA@AS8P250MB0744.EURP250.PROD.OUTLOOK.COM \ --to=andreas.rheinhardt@outlook.com \ --cc=ffmpeg-devel@ffmpeg.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel This inbox may be cloned and mirrored by anyone: git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git # If you have public-inbox 1.1+ installed, you may # initialize and index your mirror using the following commands: public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \ ffmpegdev@gitmailbox.com public-inbox-index ffmpegdev Example config snippet for mirrors. AGPL code for this site: git clone https://public-inbox.org/public-inbox.git