From: James Almer via ffmpeg-devel <ffmpeg-devel@ffmpeg.org>
To: ffmpeg-devel@ffmpeg.org
Cc: James Almer <code@ffmpeg.org>
Subject: [FFmpeg-devel] [PR] avcodec/cbs: add support for LCEVC bitstreams (PR #21789)
Date: Thu, 19 Feb 2026 01:01:17 -0000
Message-ID: <177146287775.25.119570817854103840@29965ddac10e> (raw)
PR #21789 opened by James Almer (jamrial)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21789
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21789.patch
From 464e9a505e0b55862f73fc499c742530ce2103d9 Mon Sep 17 00:00:00 2001
From: James Almer <jamrial@gmail.com>
Date: Mon, 16 Feb 2026 11:58:46 -0300
Subject: [PATCH 1/3] avcodec/bsf/extract_extradata: add support for LCEVC
Signed-off-by: James Almer <jamrial@gmail.com>
---
| 12 +++++
libavcodec/lcevc.h | 84 ++++++++++++++++++++++++++++++
2 files changed, 96 insertions(+)
create mode 100644 libavcodec/lcevc.h
--git a/libavcodec/bsf/extract_extradata.c b/libavcodec/bsf/extract_extradata.c
index 43f4d62855..d005a57769 100644
--- a/libavcodec/bsf/extract_extradata.c
+++ b/libavcodec/bsf/extract_extradata.c
@@ -29,6 +29,7 @@
#include "bytestream.h"
#include "h2645_parse.h"
#include "h264.h"
+#include "lcevc.h"
#include "startcode.h"
#include "vc1_common.h"
#include "vvc.h"
@@ -167,6 +168,9 @@ static int extract_extradata_h2645(AVBSFContext *ctx, AVPacket *pkt,
static const int extradata_nal_types_vvc[] = {
VVC_VPS_NUT, VVC_SPS_NUT, VVC_PPS_NUT,
};
+ static const int extradata_nal_types_lcevc[] = {
+ LCEVC_IDR_NUT, LCEVC_NON_IDR_NUT,
+ };
static const int extradata_nal_types_hevc[] = {
HEVC_NAL_VPS, HEVC_NAL_SPS, HEVC_NAL_PPS,
};
@@ -184,6 +188,9 @@ static int extract_extradata_h2645(AVBSFContext *ctx, AVPacket *pkt,
if (ctx->par_in->codec_id == AV_CODEC_ID_VVC) {
extradata_nal_types = extradata_nal_types_vvc;
nb_extradata_nal_types = FF_ARRAY_ELEMS(extradata_nal_types_vvc);
+ } else if (ctx->par_in->codec_id == AV_CODEC_ID_LCEVC) {
+ extradata_nal_types = extradata_nal_types_lcevc;
+ nb_extradata_nal_types = FF_ARRAY_ELEMS(extradata_nal_types_lcevc);
} else if (ctx->par_in->codec_id == AV_CODEC_ID_HEVC) {
extradata_nal_types = extradata_nal_types_hevc;
nb_extradata_nal_types = FF_ARRAY_ELEMS(extradata_nal_types_hevc);
@@ -207,6 +214,8 @@ static int extract_extradata_h2645(AVBSFContext *ctx, AVPacket *pkt,
} else if (ctx->par_in->codec_id == AV_CODEC_ID_HEVC) {
if (nal->type == HEVC_NAL_SPS) has_sps = 1;
if (nal->type == HEVC_NAL_VPS) has_vps = 1;
+ } else if (ctx->par_in->codec_id == AV_CODEC_ID_LCEVC) {
+ has_sps = 1;
} else {
if (nal->type == H264_NAL_SPS) has_sps = 1;
}
@@ -217,6 +226,7 @@ static int extract_extradata_h2645(AVBSFContext *ctx, AVPacket *pkt,
if (extradata_size &&
((ctx->par_in->codec_id == AV_CODEC_ID_VVC && has_sps) ||
+ (ctx->par_in->codec_id == AV_CODEC_ID_LCEVC && has_sps) ||
(ctx->par_in->codec_id == AV_CODEC_ID_HEVC && has_sps && has_vps) ||
(ctx->par_in->codec_id == AV_CODEC_ID_H264 && has_sps))) {
AVBufferRef *filtered_buf = NULL;
@@ -371,6 +381,7 @@ static const struct {
{ AV_CODEC_ID_CAVS, extract_extradata_mpeg4 },
{ AV_CODEC_ID_H264, extract_extradata_h2645 },
{ AV_CODEC_ID_HEVC, extract_extradata_h2645 },
+ { AV_CODEC_ID_LCEVC, extract_extradata_h2645 },
{ AV_CODEC_ID_MPEG1VIDEO, extract_extradata_mpeg12 },
{ AV_CODEC_ID_MPEG2VIDEO, extract_extradata_mpeg12 },
{ AV_CODEC_ID_MPEG4, extract_extradata_mpeg4 },
@@ -441,6 +452,7 @@ static const enum AVCodecID codec_ids[] = {
AV_CODEC_ID_CAVS,
AV_CODEC_ID_H264,
AV_CODEC_ID_HEVC,
+ AV_CODEC_ID_LCEVC,
AV_CODEC_ID_MPEG1VIDEO,
AV_CODEC_ID_MPEG2VIDEO,
AV_CODEC_ID_MPEG4,
diff --git a/libavcodec/lcevc.h b/libavcodec/lcevc.h
new file mode 100644
index 0000000000..cfe4c1e6e1
--- /dev/null
+++ b/libavcodec/lcevc.h
@@ -0,0 +1,84 @@
+/*
+ * 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
+ */
+
+/**
+ * @file
+ * LCEVC common definitions
+ */
+
+#ifndef AVCODEC_LCEVC_H
+#define AVCODEC_LCEVC_H
+
+/*
+ * Table 17 — NAL unit type codes and NAL unit type classes in
+ * ISO/IEC 23094-2:2021
+ */
+enum {
+ LCEVC_UNSPEC0_NUT = 0,
+ LCEVC_UNSPEC1_NUT = 1,
+ LCEVC_UNSPEC2_NUT = 2,
+ LCEVC_UNSPEC3_NUT = 3,
+ LCEVC_UNSPEC4_NUT = 4,
+ LCEVC_UNSPEC5_NUT = 5,
+ LCEVC_UNSPEC6_NUT = 6,
+ LCEVC_UNSPEC7_NUT = 7,
+ LCEVC_UNSPEC8_NUT = 8,
+ LCEVC_UNSPEC9_NUT = 9,
+ LCEVC_UNSPEC10_NUT = 10,
+ LCEVC_UNSPEC11_NUT = 11,
+ LCEVC_UNSPEC12_NUT = 12,
+ LCEVC_UNSPEC13_NUT = 13,
+ LCEVC_UNSPEC14_NUT = 14,
+ LCEVC_UNSPEC15_NUT = 15,
+ LCEVC_UNSPEC16_NUT = 16,
+ LCEVC_UNSPEC17_NUT = 17,
+ LCEVC_UNSPEC18_NUT = 18,
+ LCEVC_UNSPEC19_NUT = 19,
+ LCEVC_UNSPEC20_NUT = 20,
+ LCEVC_UNSPEC21_NUT = 21,
+ LCEVC_UNSPEC22_NUT = 22,
+ LCEVC_UNSPEC23_NUT = 23,
+ LCEVC_UNSPEC24_NUT = 24,
+ LCEVC_UNSPEC25_NUT = 25,
+ LCEVC_UNSPEC26_NUT = 26,
+ LCEVC_UNSPEC27_NUT = 27,
+ LCEVC_NON_IDR_NUT = 28,
+ LCEVC_IDR_NUT = 29,
+ LCEVC_RSV_NUT = 30,
+ LCEVC_UNSPEC31_NUT = 31,
+};
+
+/*
+ * Table 19 — Content of payload
+ */
+enum {
+ LCEVC_PAYLOAD_TYPE_SEQUENCE_CONFIG = 0,
+ LCEVC_PAYLOAD_TYPE_GLOBAL_CONFIG = 1,
+ LCEVC_PAYLOAD_TYPE_PICTURE_CONFIG = 2,
+ LCEVC_PAYLOAD_TYPE_ENCODED_DATA = 3,
+ LCEVC_PAYLOAD_TYPE_ENCODED_DATA_TILED = 4,
+ LCEVC_PAYLOAD_TYPE_ADDITIONAL_INFO = 5,
+ LCEVC_PAYLOAD_TYPE_FILLER = 6,
+};
+
+enum {
+ LCEVC_ADDITIONAL_INFO_TYPE_SEI = 0,
+ LCEVC_ADDITIONAL_INFO_TYPE_VUI = 1,
+};
+
+#endif /* AVCODEC_LCEVC_H */
--
2.52.0
From 5603cd678f91f8aa66b7187bb3d2738761d83a1e Mon Sep 17 00:00:00 2001
From: James Almer <jamrial@gmail.com>
Date: Mon, 16 Feb 2026 11:59:15 -0300
Subject: [PATCH 2/3] avcodec/h2645_parse: add support for LCEVC
Signed-off-by: James Almer <jamrial@gmail.com>
---
libavcodec/h2645_parse.c | 63 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 63 insertions(+)
diff --git a/libavcodec/h2645_parse.c b/libavcodec/h2645_parse.c
index fa57911c08..448f424b26 100644
--- a/libavcodec/h2645_parse.c
+++ b/libavcodec/h2645_parse.c
@@ -149,6 +149,47 @@ nsc:
return si;
}
+static const char *const lcevc_nal_type_name[32] = {
+ "UNSPEC0 ", // UNSPEC0
+ "UNSPEC1 ", // UNSPEC1
+ "UNSPEC2 ", // UNSPEC2
+ "UNSPEC3 ", // UNSPEC3
+ "UNSPEC4 ", // UNSPEC4
+ "UNSPEC5 ", // UNSPEC5
+ "UNSPEC6 ", // UNSPEC6
+ "UNSPEC7 ", // UNSPEC7
+ "UNSPEC8 ", // UNSPEC8
+ "UNSPEC9 ", // UNSPEC9
+ "UNSPEC10", // UNSPEC10
+ "UNSPEC11", // UNSPEC11
+ "UNSPEC12", // UNSPEC12
+ "UNSPEC13", // UNSPEC13
+ "UNSPEC14", // UNSPEC14
+ "UNSPEC15", // UNSPEC15
+ "UNSPEC16", // UNSPEC16
+ "UNSPEC17", // UNSPEC17
+ "UNSPEC18", // UNSPEC18
+ "UNSPEC19", // UNSPEC19
+ "UNSPEC20", // UNSPEC20
+ "UNSPEC21", // UNSPEC21
+ "UNSPEC22", // UNSPEC22
+ "UNSPEC23", // UNSPEC23
+ "UNSPEC24", // UNSPEC24
+ "UNSPEC25", // UNSPEC25
+ "UNSPEC26", // UNSPEC26
+ "UNSPEC27", // UNSPEC27
+ "LCEVC_NON_IDR", //LCEVC_NON_IDR
+ "LCEVC_IDR", // LCEVC_IDR
+ "LCEVC_RSV", // LCEVC_RSV
+ "UNSPEC31", // UNSPEC31
+};
+
+static const char *lcevc_nal_unit_name(int nal_type)
+{
+ av_assert0(nal_type >= 0 && nal_type < 32);
+ return lcevc_nal_type_name[nal_type];
+}
+
static const char *const vvc_nal_type_name[32] = {
"TRAIL_NUT", // VVC_TRAIL_NUT
"STSA_NUT", // VVC_STSA_NUT
@@ -338,6 +379,26 @@ static int get_bit_length(H2645NAL *nal, int min_size, int skip_trailing_zeros)
* @return AVERROR_INVALIDDATA if the packet is not a valid NAL unit,
* 0 otherwise
*/
+
+static int lcevc_parse_nal_header(H2645NAL *nal, void *logctx)
+{
+ GetBitContext *gb = &nal->gb;
+
+ if (get_bits1(gb) != 0) //forbidden_zero_bit
+ return AVERROR_INVALIDDATA;
+
+ if (get_bits1(gb) != 1) //forbidden_one_bit
+ return AVERROR_INVALIDDATA;
+
+ nal->type = get_bits(gb, 5);
+
+ av_log(logctx, AV_LOG_DEBUG,
+ "nal_unit_type: %d(%s)\n",
+ nal->type, lcevc_nal_unit_name(nal->type));
+
+ return 0;
+}
+
static int vvc_parse_nal_header(H2645NAL *nal, void *logctx)
{
GetBitContext *gb = &nal->gb;
@@ -582,6 +643,8 @@ int ff_h2645_packet_split(H2645Packet *pkt, const uint8_t *buf, int length,
if (codec_id == AV_CODEC_ID_VVC)
ret = vvc_parse_nal_header(nal, logctx);
+ else if (codec_id == AV_CODEC_ID_LCEVC)
+ ret = lcevc_parse_nal_header(nal, logctx);
else if (codec_id == AV_CODEC_ID_HEVC) {
ret = hevc_parse_nal_header(nal, logctx);
if (nal->nuh_layer_id == 63)
--
2.52.0
From f8af34880470dbdad8f52636b5fccb53d0a5de8b Mon Sep 17 00:00:00 2001
From: James Almer <jamrial@gmail.com>
Date: Wed, 18 Feb 2026 21:15:17 -0300
Subject: [PATCH 3/3] avcodec/cbs: add support for LCEVC bitstreams
As defined on ISO/IEC 23094-2:2021/FDAM 1:2023
Signed-off-by: James Almer <jamrial@gmail.com>
---
configure | 2 +
libavcodec/Makefile | 1 +
libavcodec/cbs.c | 6 +
libavcodec/cbs_h2645.c | 333 +++++++++++++++
libavcodec/cbs_internal.h | 4 +
libavcodec/cbs_lcevc.c | 112 +++++
libavcodec/cbs_lcevc.h | 263 ++++++++++++
libavcodec/cbs_lcevc_syntax_template.c | 569 +++++++++++++++++++++++++
libavformat/cbs.h | 1 +
9 files changed, 1291 insertions(+)
create mode 100644 libavcodec/cbs_lcevc.c
create mode 100644 libavcodec/cbs_lcevc.h
create mode 100644 libavcodec/cbs_lcevc_syntax_template.c
diff --git a/configure b/configure
index da93aaf661..e959444449 100755
--- a/configure
+++ b/configure
@@ -2693,6 +2693,7 @@ CONFIG_EXTRA="
cbs_h265
cbs_h266
cbs_jpeg
+ cbs_lcevc
cbs_mpeg2
cbs_vp8
cbs_vp9
@@ -3005,6 +3006,7 @@ cbs_h264_select="cbs"
cbs_h265_select="cbs"
cbs_h266_select="cbs"
cbs_jpeg_select="cbs"
+cbs_lcevc_select="cbs"
cbs_mpeg2_select="cbs"
cbs_vp8_select="cbs"
cbs_vp9_select="cbs"
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 3d60347a19..4c104757d1 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -91,6 +91,7 @@ OBJS-$(CONFIG_CBS_AV1) += cbs_av1.o
OBJS-$(CONFIG_CBS_H264) += cbs_h2645.o cbs_sei.o h2645_parse.o
OBJS-$(CONFIG_CBS_H265) += cbs_h2645.o cbs_sei.o h2645_parse.o
OBJS-$(CONFIG_CBS_H266) += cbs_h2645.o cbs_sei.o h2645_parse.o
+OBJS-$(CONFIG_CBS_LCEVC) += cbs_h2645.o cbs_sei.o h2645_parse.o cbs_lcevc.o
OBJS-$(CONFIG_CBS_JPEG) += cbs_jpeg.o
OBJS-$(CONFIG_CBS_MPEG2) += cbs_mpeg2.o
OBJS-$(CONFIG_CBS_VP8) += cbs_vp8.o vp8data.o
diff --git a/libavcodec/cbs.c b/libavcodec/cbs.c
index 41c8184434..b045b73b68 100644
--- a/libavcodec/cbs.c
+++ b/libavcodec/cbs.c
@@ -46,6 +46,9 @@ static const CodedBitstreamType *const cbs_type_table[] = {
#if CBS_H266
&CBS_FUNC(type_h266),
#endif
+#if CBS_LCEVC
+ &CBS_FUNC(type_lcevc),
+#endif
#if CBS_JPEG
&CBS_FUNC(type_jpeg),
#endif
@@ -76,6 +79,9 @@ const enum AVCodecID CBS_FUNC(all_codec_ids)[] = {
#if CBS_H266
AV_CODEC_ID_H266,
#endif
+#if CBS_LCEVC
+ AV_CODEC_ID_LCEVC,
+#endif
#if CBS_JPEG
AV_CODEC_ID_MJPEG,
#endif
diff --git a/libavcodec/cbs_h2645.c b/libavcodec/cbs_h2645.c
index 0dd256ada0..cc553211dc 100644
--- a/libavcodec/cbs_h2645.c
+++ b/libavcodec/cbs_h2645.c
@@ -26,6 +26,7 @@
#include "cbs_h264.h"
#include "cbs_h265.h"
#include "cbs_h266.h"
+#include "cbs_lcevc.h"
#include "h264.h"
#include "h2645_parse.h"
#include "libavutil/refstruct.h"
@@ -137,6 +138,37 @@ static int cbs_read_se_golomb(CodedBitstreamContext *ctx, GetBitContext *gbc,
return 0;
}
+static int cbs_read_multi_byte(CodedBitstreamContext *ctx, GetBitContext *gbc,
+ const char *name, uint32_t *write_to)
+{
+ uint64_t value;
+ uint32_t byte;
+ int i;
+
+ CBS_TRACE_READ_START();
+
+ value = 0;
+ for (i = 0; i < 10; i++) {
+ if (get_bits_left(gbc) < 8) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid multi byte at "
+ "%s: bitstream ended.\n", name);
+ return AVERROR_INVALIDDATA;
+ }
+ byte = get_bits(gbc, 8);
+ value = (value << 7) | (byte & 0x7f);
+ if (!(byte & 0x80))
+ break;
+ }
+
+ if (value > UINT32_MAX)
+ return AVERROR_INVALIDDATA;
+
+ CBS_TRACE_READ_END_NO_SUBSCRIPTS();
+
+ *write_to = value;
+ return 0;
+}
+
static int cbs_write_ue_golomb(CodedBitstreamContext *ctx, PutBitContext *pbc,
const char *name, const int *subscripts,
uint32_t value,
@@ -209,6 +241,32 @@ static int cbs_write_se_golomb(CodedBitstreamContext *ctx, PutBitContext *pbc,
return 0;
}
+static int cbs_write_multi_byte(CodedBitstreamContext *ctx, PutBitContext *pbc,
+ const char *name, uint32_t value)
+{
+ int len, i;
+ uint8_t byte;
+
+ CBS_TRACE_WRITE_START();
+
+ len = (av_log2(value) + 7) / 7;
+
+ for (i = len - 1; i >= 0; i--) {
+ if (put_bits_left(pbc) < 8)
+ return AVERROR(ENOSPC);
+
+ byte = value >> (7 * i) & 0x7f;
+ if (i > 0)
+ byte |= 0x80;
+
+ put_bits(pbc, 8, byte);
+ }
+
+ CBS_TRACE_WRITE_END_NO_SUBSCRIPTS();
+
+ return 0;
+}
+
// payload_extension_present() - true if we are before the last 1-bit
// in the payload structure, which must be in the last byte.
static int cbs_h265_payload_extension_present(GetBitContext *gbc, uint32_t payload_size,
@@ -234,6 +292,7 @@ static int cbs_h265_payload_extension_present(GetBitContext *gbc, uint32_t paylo
#define FUNC_H264(name) FUNC_NAME1(READWRITE, h264, name)
#define FUNC_H265(name) FUNC_NAME1(READWRITE, h265, name)
#define FUNC_H266(name) FUNC_NAME1(READWRITE, h266, name)
+#define FUNC_LCEVC(name) FUNC_NAME1(READWRITE, lcevc, name)
#define FUNC_SEI(name) FUNC_NAME1(READWRITE, sei, name)
#define SEI_FUNC(name, args) \
@@ -246,6 +305,16 @@ static int FUNC(name ## _internal)(CodedBitstreamContext *ctx, \
} \
static int FUNC(name) args
+#define LCEVC_BLOCK_FUNC(name, args) \
+static int FUNC(name) args; \
+static int FUNC(name ## _internal)(CodedBitstreamContext *ctx, \
+ RWContext *rw, void *cur, \
+ LCEVCProcessBlockState *state) \
+{ \
+ return FUNC(name)(ctx, rw, cur, state); \
+} \
+static int FUNC(name) args
+
#define SUBSCRIPTS(subs, ...) (subs > 0 ? ((int[subs + 1]){ subs, __VA_ARGS__ }) : NULL)
#define u(width, name, range_min, range_max) \
@@ -274,6 +343,8 @@ static int FUNC(name) args
xi(width, name, current->name, MIN_INT_BITS(width), MAX_INT_BITS(width), subs, __VA_ARGS__)
#define ses(name, range_min, range_max, subs, ...) \
xse(name, current->name, range_min, range_max, subs, __VA_ARGS__)
+#define mb(name) \
+ xmb(name, current->name)
#define fixed(width, name, value) do { \
av_unused uint32_t fixed_value = value; \
@@ -319,6 +390,11 @@ static int FUNC(name) args
&value, range_min, range_max)); \
var = value; \
} while (0)
+#define xmb(name, var) do { \
+ uint32_t value; \
+ CHECK(cbs_read_multi_byte(ctx, rw, #name, &value)); \
+ var = value; \
+ } while (0)
#define infer(name, value) do { \
@@ -337,6 +413,10 @@ static int cbs_h2645_read_more_rbsp_data(GetBitContext *gbc)
return 0;
}
+static int cbs_h2645_write_slice_data(CodedBitstreamContext *ctx,
+ PutBitContext *pbc, const uint8_t *data,
+ size_t data_size, int data_bit_start);
+
#define more_rbsp_data(var) ((var) = cbs_h2645_read_more_rbsp_data(rw))
#define bit_position(rw) (get_bits_count(rw))
@@ -355,6 +435,10 @@ static int cbs_h2645_read_more_rbsp_data(GetBitContext *gbc)
#include "cbs_sei_syntax_template.c"
#undef FUNC
+#define FUNC(name) FUNC_LCEVC(name)
+#include "cbs_lcevc_syntax_template.c"
+#undef FUNC
+
#undef allocate
/* The other code uses the refstruct API for the allocation
@@ -387,11 +471,13 @@ static int cbs_h2645_read_more_rbsp_data(GetBitContext *gbc)
#undef xi
#undef xue
#undef xse
+#undef xmb
#undef infer
#undef more_rbsp_data
#undef bit_position
#undef byte_alignment
#undef allocate
+#undef allocate_struct
#define WRITE
@@ -427,6 +513,10 @@ static int cbs_h2645_read_more_rbsp_data(GetBitContext *gbc)
SUBSCRIPTS(subs, __VA_ARGS__), \
value, range_min, range_max)); \
} while (0)
+#define xmb(name, var) do { \
+ uint32_t value = var; \
+ CHECK(cbs_write_multi_byte(ctx, rw, #name, value)); \
+ } while (0)
#define infer(name, value) do { \
if (current->name != (value)) { \
@@ -467,6 +557,10 @@ static int cbs_h2645_read_more_rbsp_data(GetBitContext *gbc)
#include "cbs_h266_syntax_template.c"
#undef FUNC
+#define FUNC(name) FUNC_LCEVC(name)
+#include "cbs_lcevc_syntax_template.c"
+#undef FUNC
+
#undef WRITE
#undef READWRITE
#undef RWContext
@@ -475,6 +569,7 @@ static int cbs_h2645_read_more_rbsp_data(GetBitContext *gbc)
#undef xi
#undef xue
#undef xse
+#undef xmb
#undef u
#undef i
#undef flag
@@ -736,6 +831,60 @@ static int cbs_h2645_split_fragment(CodedBitstreamContext *ctx,
if (err < 0)
return err;
}
+ } else if (header && frag->data[0] && codec_id == AV_CODEC_ID_LCEVC) {
+ // LVCC header.
+ size_t size, start, end;
+ int i, j, nb_arrays, nal_unit_type, nb_nals, version;
+
+ priv->mp4 = 1;
+
+ bytestream2_init(&gbc, frag->data, frag->data_size);
+
+ if (bytestream2_get_bytes_left(&gbc) < 14)
+ return AVERROR_INVALIDDATA;
+
+ version = bytestream2_get_byte(&gbc);
+ if (version != 1) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid LVCC header: "
+ "first byte %u.\n", version);
+ return AVERROR_INVALIDDATA;
+ }
+
+ bytestream2_skip(&gbc, 3);
+ priv->nal_length_size = (bytestream2_get_byte(&gbc) >> 6) + 1;
+
+ bytestream2_skip(&gbc, 9);
+ nb_arrays = bytestream2_get_byte(&gbc);
+
+ for (i = 0; i < nb_arrays; i++) {
+ nal_unit_type = bytestream2_get_byte(&gbc) & 0x3f;
+ nb_nals = bytestream2_get_be16(&gbc);
+
+ start = bytestream2_tell(&gbc);
+ for (j = 0; j < nb_nals; j++) {
+ if (bytestream2_get_bytes_left(&gbc) < 2)
+ return AVERROR_INVALIDDATA;
+ size = bytestream2_get_be16(&gbc);
+ if (bytestream2_get_bytes_left(&gbc) < size)
+ return AVERROR_INVALIDDATA;
+ bytestream2_skip(&gbc, size);
+ }
+ end = bytestream2_tell(&gbc);
+
+ err = ff_h2645_packet_split(&priv->read_packet,
+ frag->data + start, end - start,
+ ctx->log_ctx, 2, AV_CODEC_ID_LCEVC,
+ H2645_FLAG_IS_NALFF | H2645_FLAG_SMALL_PADDING | H2645_FLAG_USE_REF);
+ if (err < 0) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Failed to split "
+ "LVCC array %d (%d NAL units of type %d).\n",
+ i, nb_nals, nal_unit_type);
+ return err;
+ }
+ err = cbs_h2645_fragment_add_nals(ctx, frag, &priv->read_packet);
+ if (err < 0)
+ return err;
+ }
} else {
int flags = (H2645_FLAG_IS_NALFF * !!priv->mp4) | H2645_FLAG_SMALL_PADDING | H2645_FLAG_USE_REF;
// Annex B, or later MP4 with already-known parameters.
@@ -1245,6 +1394,59 @@ static int cbs_h266_read_nal_unit(CodedBitstreamContext *ctx,
return 0;
}
+
+static int cbs_lcevc_read_nal_unit(CodedBitstreamContext *ctx,
+ CodedBitstreamUnit *unit)
+{
+ GetBitContext gbc;
+ int err;
+
+ err = init_get_bits8(&gbc, unit->data, unit->data_size);
+ if (err < 0)
+ return err;
+
+ err = ff_cbs_alloc_unit_content(ctx, unit);
+ if (err < 0)
+ return err;
+
+ switch (unit->type) {
+ case LCEVC_NON_IDR_NUT:
+ case LCEVC_IDR_NUT:
+ {
+ LCEVCRawNAL *nal = unit->content;
+ LCEVCRawProcessBlockList *block_list;
+
+ err = cbs_lcevc_read_nal(ctx, &gbc, unit->content, unit->type);
+
+ if (err < 0)
+ return err;
+
+ block_list = &nal->process_block_list;
+ for (int i = 0; i < block_list->nb_blocks; i++) {
+ LCEVCRawProcessBlock *block = &block_list->blocks[i];
+ LCEVCRawEncodedData *slice;
+
+ if (block->payload_type != LCEVC_PAYLOAD_TYPE_ENCODED_DATA)
+ continue;
+
+ slice = block->payload;
+ slice->data_ref = av_buffer_ref(unit->data_ref);
+ if (!slice->data_ref)
+ return AVERROR(ENOMEM);
+ slice->data = unit->data + slice->header_size;
+ }
+
+ if (err < 0)
+ return err;
+ }
+ break;
+ default:
+ return AVERROR(ENOSYS);
+ }
+
+ return 0;
+}
+
static int cbs_h2645_write_slice_data(CodedBitstreamContext *ctx,
PutBitContext *pbc, const uint8_t *data,
size_t data_size, int data_bit_start)
@@ -1814,6 +2016,31 @@ static int cbs_h266_write_nal_unit(CodedBitstreamContext *ctx,
return 0;
}
+static int cbs_lcevc_write_nal_unit(CodedBitstreamContext *ctx,
+ CodedBitstreamUnit *unit,
+ PutBitContext *pbc)
+{
+ int err;
+
+ switch (unit->type) {
+ case LCEVC_NON_IDR_NUT:
+ case LCEVC_IDR_NUT:
+ {
+ err = cbs_lcevc_write_nal(ctx, pbc, unit->content, unit->type);
+
+ if (err < 0)
+ return err;
+ }
+ break;
+ default:
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Write unimplemented for "
+ "NAL unit type %"PRIu32".\n", unit->type);
+ return AVERROR_PATCHWELCOME;
+ }
+
+ return 0;
+}
+
static int cbs_h2645_unit_requires_zero_byte(enum AVCodecID codec_id,
CodedBitstreamUnitType type,
int nal_unit_index)
@@ -1991,6 +2218,23 @@ static av_cold void cbs_h266_close(CodedBitstreamContext *ctx)
ff_h2645_packet_uninit(&h266->common.read_packet);
}
+static av_cold void cbs_lcevc_flush(CodedBitstreamContext *ctx)
+{
+ CodedBitstreamLCEVCContext *lcevc = ctx->priv_data;
+
+ av_refstruct_unref(&lcevc->sc);
+ av_refstruct_unref(&lcevc->gc);
+ av_refstruct_unref(&lcevc->pc);
+}
+
+static av_cold void cbs_lcevc_close(CodedBitstreamContext *ctx)
+{
+ CodedBitstreamLCEVCContext *lcevc = ctx->priv_data;
+
+ cbs_lcevc_flush(ctx);
+ ff_h2645_packet_uninit(&lcevc->common.read_packet);
+}
+
static void cbs_h264_free_sei(AVRefStructOpaque unused, void *content)
{
H264RawSEI *sei = content;
@@ -2095,6 +2339,19 @@ static CodedBitstreamUnitTypeDescriptor cbs_h266_unit_types[] = {
CBS_UNIT_TYPE_END_OF_LIST
};
+static void cbs_lcevc_free_nal(AVRefStructOpaque unused, void *content)
+{
+ LCEVCRawNAL *nal = content;
+ ff_cbs_lcevc_free_process_block_list(&nal->process_block_list);
+}
+
+static CodedBitstreamUnitTypeDescriptor cbs_lcevc_unit_types[] = {
+ CBS_UNIT_TYPES_COMPLEX((LCEVC_NON_IDR_NUT, LCEVC_IDR_NUT),
+ LCEVCRawNAL, cbs_lcevc_free_nal),
+
+ CBS_UNIT_TYPE_END_OF_LIST
+};
+
const CodedBitstreamType ff_cbs_type_h264 = {
.codec_id = AV_CODEC_ID_H264,
@@ -2145,6 +2402,75 @@ const CodedBitstreamType ff_cbs_type_h266 = {
.close = &cbs_h266_close,
};
+const CodedBitstreamType ff_cbs_type_lcevc = {
+ .codec_id = AV_CODEC_ID_LCEVC,
+
+ .priv_data_size = sizeof(CodedBitstreamLCEVCContext),
+
+ .unit_types = cbs_lcevc_unit_types,
+
+ .split_fragment = &cbs_h2645_split_fragment,
+ .read_unit = &cbs_lcevc_read_nal_unit,
+ .write_unit = &cbs_lcevc_write_nal_unit,
+ .assemble_fragment = &cbs_h2645_assemble_fragment,
+
+ .flush = &cbs_lcevc_flush,
+ .close = &cbs_lcevc_close,
+};
+
+// Macro for the read/write pair.
+#define LCEVC_PROCESS_BLOCK_RW(codec, name) \
+ .read = cbs_ ## codec ## _read_ ## name ## _internal, \
+ .write = cbs_ ## codec ## _write_ ## name ## _internal
+
+static const LCEVCProcessBlockTypeDescriptor cbs_lcevc_process_block_types[] = {
+ {
+ LCEVC_PAYLOAD_TYPE_SEQUENCE_CONFIG,
+ sizeof(LCEVCRawSequenceConfig),
+ LCEVC_PROCESS_BLOCK_RW(lcevc, sequence_config),
+ },
+ {
+ LCEVC_PAYLOAD_TYPE_GLOBAL_CONFIG,
+ sizeof(LCEVCRawGlobalConfig),
+ LCEVC_PROCESS_BLOCK_RW(lcevc, global_config),
+ },
+ {
+ LCEVC_PAYLOAD_TYPE_PICTURE_CONFIG,
+ sizeof(LCEVCRawPictureConfig),
+ LCEVC_PROCESS_BLOCK_RW(lcevc, picture_config),
+ },
+ {
+ LCEVC_PAYLOAD_TYPE_ENCODED_DATA,
+ sizeof(LCEVCRawEncodedData),
+ LCEVC_PROCESS_BLOCK_RW(lcevc, encoded_data),
+ },
+ {
+ LCEVC_PAYLOAD_TYPE_ADDITIONAL_INFO,
+ sizeof(LCEVCRawAdditionalInfo),
+ LCEVC_PROCESS_BLOCK_RW(lcevc, additional_info),
+ },
+ {
+ LCEVC_PAYLOAD_TYPE_FILLER,
+ sizeof(LCEVCRawFiller),
+ LCEVC_PROCESS_BLOCK_RW(lcevc, filler),
+ },
+ LCEVC_PROCESS_BLOCK_TYPE_END,
+};
+
+const LCEVCProcessBlockTypeDescriptor
+ *ff_cbs_lcevc_process_block_find_type(CodedBitstreamContext *ctx,
+ int payload_type)
+{
+ int i;
+
+ for (i = 0; cbs_lcevc_process_block_types[i].payload_type >= 0; i++) {
+ if (cbs_lcevc_process_block_types[i].payload_type == payload_type)
+ return &cbs_lcevc_process_block_types[i];
+ }
+
+ return NULL;
+}
+
// Macro for the read/write pair.
#define SEI_MESSAGE_RW(codec, name) \
.read = cbs_ ## codec ## _read_ ## name ## _internal, \
@@ -2328,6 +2654,10 @@ static const SEIMessageTypeDescriptor cbs_sei_h266_types[] = {
SEI_MESSAGE_TYPE_END
};
+static const SEIMessageTypeDescriptor cbs_sei_lcevc_types[] = {
+ SEI_MESSAGE_TYPE_END
+};
+
static const SEIMessageTypeDescriptor cbs_sei_h274_types[] = {
{
SEI_TYPE_FILM_GRAIN_CHARACTERISTICS,
@@ -2366,6 +2696,9 @@ const SEIMessageTypeDescriptor *ff_cbs_sei_find_type(CodedBitstreamContext *ctx,
case AV_CODEC_ID_H266:
codec_list = cbs_sei_h266_types;
break;
+ case AV_CODEC_ID_LCEVC:
+ codec_list = cbs_sei_lcevc_types;
+ break;
default:
return NULL;
}
diff --git a/libavcodec/cbs_internal.h b/libavcodec/cbs_internal.h
index 8ca53ff3ce..2af8075f78 100644
--- a/libavcodec/cbs_internal.h
+++ b/libavcodec/cbs_internal.h
@@ -57,6 +57,9 @@
#ifndef CBS_H266
#define CBS_H266 CONFIG_CBS_H266
#endif
+#ifndef CBS_LCEVC
+#define CBS_LCEVC CONFIG_CBS_LCEVC
+#endif
#ifndef CBS_JPEG
#define CBS_JPEG CONFIG_CBS_JPEG
#endif
@@ -391,6 +394,7 @@ extern const CodedBitstreamType CBS_FUNC(type_av1);
extern const CodedBitstreamType CBS_FUNC(type_h264);
extern const CodedBitstreamType CBS_FUNC(type_h265);
extern const CodedBitstreamType CBS_FUNC(type_h266);
+extern const CodedBitstreamType CBS_FUNC(type_lcevc);
extern const CodedBitstreamType CBS_FUNC(type_jpeg);
extern const CodedBitstreamType CBS_FUNC(type_mpeg2);
extern const CodedBitstreamType CBS_FUNC(type_vp8);
diff --git a/libavcodec/cbs_lcevc.c b/libavcodec/cbs_lcevc.c
new file mode 100644
index 0000000000..7dcf709c94
--- /dev/null
+++ b/libavcodec/cbs_lcevc.c
@@ -0,0 +1,112 @@
+/*
+ * 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 "libavutil/mem.h"
+#include "cbs.h"
+#include "cbs_internal.h"
+#include "cbs_lcevc.h"
+#include "libavutil/refstruct.h"
+
+static void free_picture_config(AVRefStructOpaque unused, void *obj)
+{
+ LCEVCRawPictureConfig *picture_config = obj;
+
+ av_refstruct_unref(&picture_config->gc);
+}
+
+static void free_encoded_data(AVRefStructOpaque unused, void *obj)
+{
+ LCEVCRawEncodedData *slice = obj;
+
+ av_buffer_unref(&slice->data_ref);
+
+ av_refstruct_unref(&slice->sc);
+ av_refstruct_unref(&slice->gc);
+ av_refstruct_unref(&slice->pc);
+}
+
+static void free_additional_info(AVRefStructOpaque unused, void *obj)
+{
+ LCEVCRawAdditionalInfo *additional_info = obj;
+ SEIRawMessage *message = &additional_info->sei;
+
+ av_refstruct_unref(&additional_info->payload_ref);
+ av_refstruct_unref(&message->payload_ref);
+ av_refstruct_unref(&message->extension_data);
+}
+
+int ff_cbs_lcevc_alloc_process_block_payload(LCEVCRawProcessBlock *block,
+ const LCEVCProcessBlockTypeDescriptor *desc)
+{
+ void (*free_func)(AVRefStructOpaque, void*);
+
+ av_assert0(block->payload == NULL &&
+ block->payload_ref == NULL);
+ block->payload_type = desc->payload_type;
+
+ if (desc->payload_type == LCEVC_PAYLOAD_TYPE_PICTURE_CONFIG)
+ free_func = &free_picture_config;
+ else if (desc->payload_type == LCEVC_PAYLOAD_TYPE_ENCODED_DATA)
+ free_func = &free_encoded_data;
+ else if (desc->payload_type == LCEVC_PAYLOAD_TYPE_ADDITIONAL_INFO)
+ free_func = &free_additional_info;
+ else
+ free_func = NULL;
+
+ block->payload_ref = av_refstruct_alloc_ext(desc->payload_size, 0,
+ NULL, free_func);
+ if (!block->payload_ref)
+ return AVERROR(ENOMEM);
+ block->payload = block->payload_ref;
+
+ return 0;
+}
+
+int ff_cbs_lcevc_list_add(LCEVCRawProcessBlockList *list)
+{
+ void *ptr;
+ int old_count = list->nb_blocks_allocated;
+
+ av_assert0(list->nb_blocks <= old_count);
+ if (list->nb_blocks + 1 > old_count) {
+ int new_count = 2 * old_count + 1;
+
+ ptr = av_realloc_array(list->blocks,
+ new_count, sizeof(*list->blocks));
+ if (!ptr)
+ return AVERROR(ENOMEM);
+
+ list->blocks = ptr;
+ list->nb_blocks_allocated = new_count;
+
+ // Zero the newly-added entries.
+ memset(list->blocks + old_count, 0,
+ (new_count - old_count) * sizeof(*list->blocks));
+ }
+ ++list->nb_blocks;
+ return 0;
+}
+
+void ff_cbs_lcevc_free_process_block_list(LCEVCRawProcessBlockList *list)
+{
+ for (int i = 0; i < list->nb_blocks; i++) {
+ LCEVCRawProcessBlock *block = &list->blocks[i];
+ av_refstruct_unref(&block->payload_ref);
+ }
+ av_free(list->blocks);
+}
diff --git a/libavcodec/cbs_lcevc.h b/libavcodec/cbs_lcevc.h
new file mode 100644
index 0000000000..66b5cbd081
--- /dev/null
+++ b/libavcodec/cbs_lcevc.h
@@ -0,0 +1,263 @@
+/*
+ * 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_CBS_LCEVC_H
+#define AVCODEC_CBS_LCEVC_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "cbs_h2645.h"
+#include "cbs_sei.h"
+#include "lcevc.h"
+
+typedef struct LCEVCRawNALUnitHeader {
+ uint8_t nal_unit_type;
+ uint16_t reserved_flag;
+} LCEVCRawNALUnitHeader;
+
+typedef struct LCEVCRawSequenceConfig {
+ uint8_t profile_idc;
+ uint8_t level_idc;
+ uint8_t sublevel_idc;
+ uint8_t conformance_window_flag;
+ uint8_t reserved_zeros_5bit;
+ uint8_t extended_profile_idc;
+ uint8_t extended_level_idc;
+ uint8_t reserved_zeros_1bit;
+ uint32_t conf_win_left_offset;
+ uint32_t conf_win_right_offset;
+ uint32_t conf_win_top_offset;
+ uint32_t conf_win_bottom_offset;
+} LCEVCRawSequenceConfig;
+
+typedef struct LCEVCRawGlobalConfig {
+ uint8_t processed_planes_type_flag;
+ uint8_t resolution_type;
+ uint8_t transform_type;
+ uint8_t chroma_sampling_type;
+ uint8_t base_depth_type;
+ uint8_t enhancement_depth_type;
+ uint8_t temporal_step_width_modifier_signalled_flag;
+ uint8_t predicted_residual_mode_flag;
+ uint8_t temporal_tile_intra_signalling_enabled_flag;
+ uint8_t temporal_enabled_flag;
+ uint8_t upsample_type;
+ uint8_t level1_filtering_signalled_flag;
+ uint8_t scaling_mode_level1;
+ uint8_t scaling_mode_level2;
+ uint8_t tile_dimensions_type;
+ uint8_t user_data_enabled;
+ uint8_t level1_depth_flag;
+ uint8_t chroma_step_width_flag;
+ uint8_t planes_type;
+ uint8_t reserved_zeros_4bit;
+ uint8_t temporal_step_width_modifier;
+ uint16_t upsampler_coeff1;
+ uint16_t upsampler_coeff2;
+ uint16_t upsampler_coeff3;
+ uint16_t upsampler_coeff4;
+ uint8_t level1_filtering_first_coefficient;
+ uint8_t level1_filtering_second_coefficient;
+ uint16_t custom_tile_width;
+ uint16_t custom_tile_height;
+ uint16_t reserved_zeros_5bit;
+ uint8_t compression_type_entropy_enabled_per_tile_flag;
+ uint8_t compression_type_size_per_tile;
+ uint16_t custom_resolution_width;
+ uint16_t custom_resolution_height;
+ uint8_t chroma_step_width_multiplier;
+} LCEVCRawGlobalConfig;
+
+typedef struct LCEVCRawPictureConfig {
+ uint8_t no_enhancement_bit_flag;
+ uint8_t quant_matrix_mode;
+ uint8_t dequant_offset_signalled_flag;
+ uint8_t picture_type_bit_flag;
+ uint8_t temporal_refresh_bit_flag;
+ uint8_t step_width_sublayer1_enabled_flag;
+ uint16_t step_width_sublayer2;
+ uint8_t dithering_control_flag;
+ uint8_t reserved_zeros_4bit;
+ uint8_t temporal_signalling_present_flag;
+ uint8_t field_type_bit_flag;
+ uint8_t reserved_zeros_7bit;
+ uint16_t step_width_sublayer1;
+ uint8_t level1_filtering_enabled_flag;
+ uint8_t qm_coefficient_0[16];
+ uint8_t qm_coefficient_1[16];
+ uint8_t dequant_offset_mode_flag;
+ uint8_t dequant_offset;
+ uint8_t dithering_type;
+ uint8_t reserverd_zero;
+ uint8_t dithering_strength;
+ uint8_t reserved_zeros_5bit;
+
+ LCEVCRawGlobalConfig *gc; ///< RefStruct references
+} LCEVCRawPictureConfig;
+
+typedef struct LCEVCRawEncodedData {
+ LCEVCRawNALUnitHeader nal_unit_header;
+
+ uint8_t surfaces_entropy_enabled_flag[3][3][16];
+ uint8_t surfaces_rle_only_flag[3][3][16];
+ uint8_t temporal_surfaces_entropy_enabled_flag[3];
+ uint8_t temporal_surfaces_rle_only_flag[3];
+
+ uint8_t *data;
+ AVBufferRef *data_ref;
+ size_t header_size;
+ size_t data_size;
+
+ LCEVCRawSequenceConfig *sc; ///< RefStruct references
+ LCEVCRawGlobalConfig *gc; ///< RefStruct references
+ LCEVCRawPictureConfig *pc; ///< RefStruct references
+} LCEVCRawEncodedData;
+
+typedef struct LCEVCRawVUI {
+ uint8_t aspect_ratio_info_present_flag;
+ uint8_t aspect_ratio_idc;
+ uint16_t sar_width;
+ uint8_t sar_height;
+ uint8_t overscan_info_present_flag;
+ uint8_t overscan_appropriate_flag;
+ uint8_t video_signal_type_present_flag;
+ uint8_t video_format;
+ uint8_t video_full_range_flag;
+ uint8_t colour_description_present_flag;
+ uint8_t colour_primaries;
+ uint8_t transfer_characteristics;
+ uint8_t matrix_coefficients;
+ uint8_t chroma_loc_info_present_flag;
+ uint8_t chroma_sample_loc_type_top_field;
+ uint8_t chroma_sample_loc_type_bottom_field;
+} LCEVCRawVUI;
+
+typedef struct LCEVCRawSharpenFilter {
+ uint8_t sharpen_type;
+ uint8_t sharpen_strength;
+} LCEVCRawSharpenFilter;
+
+typedef struct LCEVCRawAdditionalInfo {
+ uint8_t additional_info_type;
+ uint8_t payload_type;
+
+ SEIRawMessage sei;
+ LCEVCRawVUI vui;
+ LCEVCRawSharpenFilter sfilter;
+
+ uint32_t payload_size;
+ void *payload;
+ void *payload_ref; ///< RefStruct reference
+} LCEVCRawAdditionalInfo;
+
+typedef struct LCEVCRawFiller {
+ uint32_t filler_size;
+} LCEVCRawFiller;
+
+typedef struct LCEVCRawProcessBlock {
+ uint32_t payload_type;
+ uint32_t payload_size;
+ void *payload;
+ void *payload_ref; ///< RefStruct reference
+} LCEVCRawProcessBlock;
+
+typedef struct LCEVCRawProcessBlockList {
+ LCEVCRawProcessBlock *blocks;
+ int nb_blocks;
+ int nb_blocks_allocated;
+} LCEVCRawProcessBlockList;
+
+typedef struct LCEVCRawNAL {
+ LCEVCRawNALUnitHeader nal_unit_header;
+
+ LCEVCRawProcessBlockList process_block_list;
+} LCEVCRawNAL;
+
+typedef struct LCEVCProcessBlockState {
+ // The type of the payload being written.
+ uint32_t payload_type;
+ // When reading, contains the size of the payload to allow finding the
+ // end of variable-length fields (such as user_data_payload_byte[]).
+ // (When writing, the size will be derived from the total number of
+ // bytes actually written.)
+ uint32_t payload_size;
+} LCEVCProcessBlockState;
+
+typedef int (*LCEVCRawProcessBlockReadFunction)(CodedBitstreamContext *ctx,
+ struct GetBitContext *rw,
+ void *current,
+ LCEVCProcessBlockState *sei);
+
+typedef int (*LCEVCRawProcessBlockWriteFunction)(CodedBitstreamContext *ctx,
+ struct PutBitContext *rw,
+ void *current,
+ LCEVCProcessBlockState *sei);
+
+typedef struct LCEVCProcessBlockTypeDescriptor {
+ // Payload type for the block. (-1 in this field ends a list.)
+ int payload_type;
+ // Size of the decomposed structure.
+ size_t payload_size;
+ // Read bitstream into Process Block.
+ LCEVCRawProcessBlockReadFunction read;
+ // Write bitstream from Process Block.
+ LCEVCRawProcessBlockWriteFunction write;
+} LCEVCProcessBlockTypeDescriptor;
+
+// End-of-list sentinel element.
+#define LCEVC_PROCESS_BLOCK_TYPE_END { .payload_type = -1 }
+
+typedef struct CodedBitstreamLCEVCContext {
+ // Reader/writer context in common with the H.264 implementation.
+ CodedBitstreamH2645Context common;
+
+ // All currently available parameter sets. These are updated when
+ // any parameter set NAL unit is read/written with this context.
+ LCEVCRawSequenceConfig *sc; ///< RefStruct references
+ LCEVCRawGlobalConfig *gc; ///< RefStruct references
+ LCEVCRawPictureConfig *pc; ///< RefStruct references
+} CodedBitstreamLCEVCContext;
+
+/**
+ * Find the type descriptor for the given payload type.
+ *
+ * Returns NULL if the payload type is not known.
+ */
+const LCEVCProcessBlockTypeDescriptor *ff_cbs_lcevc_process_block_find_type(CodedBitstreamContext *ctx,
+ int payload_type);
+
+/**
+ * Allocate a new payload for the given Process Block.
+ */
+int ff_cbs_lcevc_alloc_process_block_payload(LCEVCRawProcessBlock *block,
+ const LCEVCProcessBlockTypeDescriptor *desc);
+
+/**
+ * Allocate a new empty Process Block in a block list.
+ *
+ * The new block is in place nb_blocks - 1.
+ */
+int ff_cbs_lcevc_list_add(LCEVCRawProcessBlockList *list);
+
+/**
+ * Free all Process Block in a block list.
+ */
+void ff_cbs_lcevc_free_process_block_list(LCEVCRawProcessBlockList *list);
+
+#endif /* AVCODEC_CBS_LCEVC_H */
diff --git a/libavcodec/cbs_lcevc_syntax_template.c b/libavcodec/cbs_lcevc_syntax_template.c
new file mode 100644
index 0000000000..552a9e271d
--- /dev/null
+++ b/libavcodec/cbs_lcevc_syntax_template.c
@@ -0,0 +1,569 @@
+/*
+ * 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
+ */
+
+static int FUNC(byte_alignment)(CodedBitstreamContext *ctx, RWContext *rw)
+{
+ int err;
+
+ // ISO/IEC 23094-2:2021/FDAM 1:2023(E) 7.3.12
+ while (byte_alignment(rw) != 0)
+ fixed(1, alignment_bit_equal_to_zero, 0);
+
+ return 0;
+}
+
+static int FUNC(rbsp_trailing_bits)(CodedBitstreamContext *ctx, RWContext *rw)
+{
+ int err;
+
+ fixed(1, rbsp_stop_one_bit, 1);
+ while (byte_alignment(rw) != 0)
+ fixed(1, rbsp_alignment_zero_bit, 0);
+
+ return 0;
+}
+
+static int FUNC(nal_unit_header)(CodedBitstreamContext *ctx, RWContext *rw,
+ LCEVCRawNALUnitHeader *current,
+ uint32_t valid_type_mask)
+{
+ int err;
+
+ fixed(1, forbidden_zero_bit, 0);
+ fixed(1, forbidden_one_bit, 1);
+ ub(5, nal_unit_type);
+
+ if (!(1 << current->nal_unit_type & valid_type_mask)) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid NAL unit type %d.\n",
+ current->nal_unit_type);
+ return AVERROR_INVALIDDATA;
+ }
+
+ ub(9, reserved_flag);
+
+ return 0;
+}
+
+LCEVC_BLOCK_FUNC(global_config, (CodedBitstreamContext *ctx, RWContext *rw,
+ LCEVCRawGlobalConfig *current,
+ LCEVCProcessBlockState *state))
+{
+ CodedBitstreamLCEVCContext *priv = ctx->priv_data;
+ int err;
+
+ HEADER("Global Config");
+
+ flag(processed_planes_type_flag);
+ ub(6, resolution_type);
+ ub(1, transform_type);
+ ub(2, chroma_sampling_type);
+ ub(2, base_depth_type);
+ ub(2, enhancement_depth_type);
+ flag(temporal_step_width_modifier_signalled_flag);
+ flag(predicted_residual_mode_flag);
+ flag(temporal_tile_intra_signalling_enabled_flag);
+ flag(temporal_enabled_flag);
+ ub(3, upsample_type);
+ flag(level1_filtering_signalled_flag);
+ ub(2, scaling_mode_level1);
+ ub(2, scaling_mode_level2);
+ ub(2, tile_dimensions_type);
+ ub(2, user_data_enabled);
+ flag(level1_depth_flag);
+ flag(chroma_step_width_flag);
+
+ if (current->processed_planes_type_flag) {
+ ub(4, planes_type);
+ ub(4, reserved_zeros_4bit);
+ } else
+ infer(planes_type, 0);
+
+ if (current->temporal_step_width_modifier_signalled_flag) {
+ ub(8, temporal_step_width_modifier);
+ }
+
+ if (current->upsample_type == 4) {
+ ub(16, upsampler_coeff1);
+ ub(16, upsampler_coeff2);
+ ub(16, upsampler_coeff3);
+ ub(16, upsampler_coeff4);
+ }
+
+ if (current->level1_filtering_signalled_flag) {
+ ub(4, level1_filtering_first_coefficient);
+ ub(4, level1_filtering_second_coefficient);
+ }
+
+ if (current->tile_dimensions_type > 0) {
+ if (current->tile_dimensions_type == 3) {
+ ub(16, custom_tile_width);
+ ub(16, custom_tile_height);
+ }
+ ub(5, reserved_zeros_5bit);
+ flag(compression_type_entropy_enabled_per_tile_flag);
+ ub(2, compression_type_size_per_tile);
+ }
+
+ if (current->resolution_type == 63) {
+ ub(16, custom_resolution_width);
+ ub(16, custom_resolution_height);
+ }
+ if (current->chroma_step_width_flag) {
+ ub(8, chroma_step_width_multiplier);
+ } else {
+ infer(chroma_step_width_multiplier, 64);
+ }
+
+ av_refstruct_replace(&priv->gc, current);
+
+ return 0;
+}
+
+LCEVC_BLOCK_FUNC(sequence_config, (CodedBitstreamContext *ctx, RWContext *rw,
+ LCEVCRawSequenceConfig *current,
+ LCEVCProcessBlockState *state))
+{
+ CodedBitstreamLCEVCContext *priv = ctx->priv_data;
+ int err;
+
+ HEADER("Sequence Config");
+
+ ub(4, profile_idc);
+ ub(4, level_idc);
+ ub(2, sublevel_idc);
+ flag(conformance_window_flag);
+ ub(5, reserved_zeros_5bit);
+
+ if (current->profile_idc == 15 || current->level_idc == 15) {
+ ub(3, profile_idc);
+ ub(4, level_idc);
+ ub(1, reserved_zeros_1bit);
+ }
+ if (current->conformance_window_flag == 1) {
+ mb(conf_win_left_offset);
+ mb(conf_win_right_offset);
+ mb(conf_win_top_offset);
+ mb(conf_win_bottom_offset);
+ }
+
+ av_refstruct_replace(&priv->sc, current);
+
+ return 0;
+}
+
+LCEVC_BLOCK_FUNC(picture_config, (CodedBitstreamContext *ctx, RWContext *rw,
+ LCEVCRawPictureConfig *current,
+ LCEVCProcessBlockState *state))
+{
+ CodedBitstreamLCEVCContext *priv = ctx->priv_data;
+ int nlayers, err;
+
+ HEADER("Picture Config");
+
+ if (!priv->gc)
+ return AVERROR_INVALIDDATA;
+
+ flag(no_enhancement_bit_flag);
+ if (current->no_enhancement_bit_flag == 0) {
+ ub(3, quant_matrix_mode);
+ flag(dequant_offset_signalled_flag);
+ flag(picture_type_bit_flag);
+ flag(temporal_refresh_bit_flag);
+ flag(step_width_sublayer1_enabled_flag);
+ ub(15, step_width_sublayer2);
+ flag(dithering_control_flag);
+ infer(temporal_signalling_present_flag, 0);
+ } else {
+ ub(4, reserved_zeros_4bit);
+ flag(picture_type_bit_flag);
+ flag(temporal_refresh_bit_flag);
+ flag(temporal_signalling_present_flag);
+ }
+
+ if (current->picture_type_bit_flag == 1) {
+ flag(field_type_bit_flag);
+ ub(7, reserved_zeros_7bit);
+ }
+
+ if (current->step_width_sublayer1_enabled_flag == 1) {
+ ub(15, step_width_sublayer1);
+ flag(level1_filtering_enabled_flag);
+ }
+
+ nlayers = priv->gc->transform_type ? 16 : 4;
+ if (current->quant_matrix_mode == 2 ||
+ current->quant_matrix_mode == 3 ||
+ current->quant_matrix_mode == 5) {
+ for (int layer_idx = 0; layer_idx < nlayers; layer_idx++)
+ ubs(8, qm_coefficient_0[layer_idx], 1, layer_idx);
+ }
+
+ if (current->quant_matrix_mode == 4 || current->quant_matrix_mode == 5) {
+ for (int layer_idx = 0; layer_idx < nlayers; layer_idx++)
+ ubs(8, qm_coefficient_1[layer_idx], 1, layer_idx);
+ }
+
+ if (current->dequant_offset_signalled_flag) {
+ flag(dequant_offset_mode_flag);
+ ub(7, dequant_offset);
+ }
+
+ if (current->dithering_control_flag == 1) {
+ ub(2, dithering_type);
+ ub(1, reserverd_zero);
+ if (current->dithering_type != 0) {
+ ub(5, dithering_strength);
+ } else {
+ ub(5, reserved_zeros_5bit);
+ }
+ }
+
+ av_refstruct_replace(&priv->pc, current);
+ av_refstruct_replace(¤t->gc, priv->gc);
+
+ return 0;
+}
+
+LCEVC_BLOCK_FUNC(encoded_data, (CodedBitstreamContext *ctx, RWContext *rw,
+ LCEVCRawEncodedData *current,
+ LCEVCProcessBlockState *state))
+{
+ CodedBitstreamLCEVCContext *priv = ctx->priv_data;
+ int nplanes, nlayers, err;
+#ifdef READ
+ int start = get_bits_count(rw);
+#endif
+
+ HEADER("Encoded Data");
+
+ if (!priv->gc || !priv->pc)
+ return AVERROR_INVALIDDATA;
+
+ nplanes = priv->gc->planes_type ? 3 : 1;
+ nlayers = priv->gc->transform_type ? 16 : 4;
+ for (int plane_idx = 0; plane_idx < nplanes; plane_idx++) {
+ if (priv->pc->no_enhancement_bit_flag == 0) {
+ for (int level_idx = 1; level_idx <= 2; level_idx++) {
+ for (int layer_idx = 0; layer_idx < nlayers; layer_idx++) {
+ ubs(1, surfaces_entropy_enabled_flag[plane_idx][level_idx][layer_idx], 3, plane_idx, level_idx, layer_idx);
+ ubs(1, surfaces_rle_only_flag[plane_idx][level_idx][layer_idx], 3, plane_idx, level_idx, layer_idx);
+ }
+ }
+ }
+ if (priv->pc->temporal_signalling_present_flag == 1) {
+ ubs(1, temporal_surfaces_entropy_enabled_flag[plane_idx], 1, plane_idx);
+ ubs(1, temporal_surfaces_rle_only_flag[plane_idx], 1, plane_idx);
+ }
+ }
+
+ CHECK(FUNC(byte_alignment)(ctx, rw));
+
+#ifdef READ
+ if (!cbs_h2645_read_more_rbsp_data(rw))
+ return AVERROR_INVALIDDATA;
+
+ int pos = get_bits_count(rw) - start;
+ int len = state->payload_size;
+
+ current->header_size = pos / 8;
+ current->data_size = len - pos / 8;
+#else
+ err = cbs_h2645_write_slice_data(ctx, rw, current->data,
+ current->data_size, 0);
+ if (err < 0)
+ return err;
+#endif
+
+ av_refstruct_replace(¤t->sc, priv->sc);
+ av_refstruct_replace(¤t->gc, priv->gc);
+ av_refstruct_replace(¤t->pc, priv->pc);
+
+ return 0;
+}
+
+static int FUNC(sei_payload)(CodedBitstreamContext *ctx, RWContext *rw,
+ SEIRawMessage *current,
+ int payload_type, int payload_size)
+{
+ int sei_type;
+ int err;
+
+ if (payload_type == 1)
+ sei_type = SEI_TYPE_MASTERING_DISPLAY_COLOUR_VOLUME;
+ else if (payload_type == 2)
+ sei_type = SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO;
+ else if (payload_type == 4)
+ sei_type = SEI_TYPE_USER_DATA_REGISTERED_ITU_T_T35;
+ else if (payload_type == 5)
+ sei_type = SEI_TYPE_USER_DATA_UNREGISTERED;
+
+ current->payload_type = sei_type;
+ current->payload_size = payload_size;
+
+ CHECK(FUNC_SEI(message)(ctx, rw, current));
+
+ return 0;
+}
+
+static int FUNC(vui_parameters)(CodedBitstreamContext *ctx, RWContext *rw,
+ LCEVCRawVUI *current)
+{
+ int err;
+
+ HEADER("VUI Parameters");
+
+ flag(aspect_ratio_info_present_flag);
+ if (current->aspect_ratio_info_present_flag) {
+ ub(8, aspect_ratio_idc);
+
+ if (current->aspect_ratio_idc == 255) {
+ ub(16, sar_width);
+ ub(16, sar_height);
+ }
+ }
+
+ flag(overscan_info_present_flag);
+ if (current->overscan_info_present_flag)
+ flag(overscan_appropriate_flag);
+
+ flag(video_signal_type_present_flag);
+ if (current->video_signal_type_present_flag) {
+ ub(3, video_format);
+ flag(video_full_range_flag);
+ flag(colour_description_present_flag);
+ if (current->colour_description_present_flag) {
+ ub(8, colour_primaries);
+ ub(8, transfer_characteristics);
+ ub(8, matrix_coefficients);
+ }
+ }
+ flag(chroma_loc_info_present_flag);
+ if (current->chroma_loc_info_present_flag) {
+ ue(chroma_sample_loc_type_top_field, 0, 5);
+ ue(chroma_sample_loc_type_bottom_field, 0, 5);
+ }
+
+ return 0;
+}
+
+LCEVC_BLOCK_FUNC(additional_info, (CodedBitstreamContext *ctx, RWContext *rw,
+ LCEVCRawAdditionalInfo *current,
+ LCEVCProcessBlockState *state))
+{
+ int err, i;
+
+ HEADER("Additional Info");
+
+ ub(8, additional_info_type);
+
+ if (current->additional_info_type == LCEVC_ADDITIONAL_INFO_TYPE_SEI) {
+ ub(8, payload_type);
+ CHECK(FUNC(sei_payload)(ctx, rw, ¤t->sei,
+ current->payload_type, state->payload_size - 2));
+ } else if (current->additional_info_type == LCEVC_ADDITIONAL_INFO_TYPE_VUI)
+ CHECK(FUNC(vui_parameters)(ctx, rw, ¤t->vui));
+ else {
+ uint8_t *data;
+
+ allocate(current->payload_ref, state->payload_size - 1);
+ current->payload_size = state->payload_size - 1;
+ current->payload = current->payload_ref;
+ data = current->payload;
+
+ for (i = 0; i < current->payload_size; i++)
+ xu(8, additional_info_byte[i], data[i], 0, 255, 1, i);
+ }
+
+ return 0;
+}
+
+LCEVC_BLOCK_FUNC(filler, (CodedBitstreamContext *ctx, RWContext *rw,
+ LCEVCRawFiller *current,
+ LCEVCProcessBlockState *state))
+{
+ int err;
+
+ HEADER("Filler");
+
+
+#ifdef READ
+ while (show_bits(rw, 8) == 0xaa) {
+ fixed(8, filler_byte, 0xaa);
+ ++current->filler_size;
+ }
+ if (state->payload_size != current->filler_size)
+ return AVERROR_INVALIDDATA;
+
+#else
+ for (int i = 0; i < current->filler_size; i++)
+ fixed(8, filler_byte, 0xaa);
+#endif
+
+ return 0;
+}
+
+static int FUNC(process_block)(CodedBitstreamContext *ctx, RWContext *rw,
+ LCEVCRawProcessBlock *current)
+{
+ const LCEVCProcessBlockTypeDescriptor *desc;
+ int err, i;
+
+ desc = ff_cbs_lcevc_process_block_find_type(ctx, current->payload_type);
+ if (desc) {
+ LCEVCProcessBlockState state = {
+ .payload_type = current->payload_type,
+ .payload_size = current->payload_size,
+ };
+#ifdef READ
+ CHECK(ff_cbs_lcevc_alloc_process_block_payload(current, desc));
+#else
+ int start_position = bit_position(rw);
+#endif
+
+ CHECK(desc->READWRITE(ctx, rw, current->payload, &state));
+
+ av_assert0(!byte_alignment(rw));
+#ifdef WRITE
+ current->payload_size = (bit_position(rw) - start_position) / 8;
+#endif
+ } else {
+ uint8_t *data;
+
+#ifdef READ
+ allocate(current->payload_ref, current->payload_size);
+ current->payload = current->payload_ref;
+#else
+ allocate(current->payload, current->payload_size);
+#endif
+ data = current->payload;
+
+ for (i = 0; i < current->payload_size; i++)
+ xu(8, payload_byte[i], data[i], 0, 255, 1, i);
+ }
+
+ return 0;
+}
+
+static int FUNC(process_block_list)(CodedBitstreamContext *ctx, RWContext *rw,
+ LCEVCRawProcessBlockList *current)
+{
+ LCEVCRawProcessBlock *block;
+ int err, k;
+
+#ifdef READ
+ for (k = 0;; k++) {
+ int payload_size_type;
+ int payload_type;
+ uint32_t payload_size;
+ uint32_t tmp;
+ GetBitContext payload_gbc;
+
+ HEADER("Process Block");
+
+ xu(3, payload_size_type, tmp, 0, MAX_UINT_BITS(3), 0);
+ payload_size_type = tmp;
+ xu(5, payload_type, tmp, 0, MAX_UINT_BITS(5), 0);
+ payload_type = tmp;
+
+ if (payload_size_type == 6) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "payload_size_type == 6\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ payload_size = payload_size_type;
+ if (payload_size_type == 7)
+ xmb(custom_byte_size, payload_size);
+
+ // There must be space remaining for the payload
+ if (payload_size > get_bits_left(rw) / 8) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR,
+ "Invalid process block: payload_size too large "
+ "(%"PRIu32" bytes).\n", payload_size);
+ return AVERROR_INVALIDDATA;
+ }
+ CHECK(init_get_bits(&payload_gbc, rw->buffer,
+ get_bits_count(rw) + 8 * payload_size));
+ skip_bits_long(&payload_gbc, get_bits_count(rw));
+
+ CHECK(ff_cbs_lcevc_list_add(current));
+ block = ¤t->blocks[k];
+
+ block->payload_type = payload_type;
+ block->payload_size = payload_size;
+
+ CHECK(FUNC(process_block)(ctx, &payload_gbc, block));
+
+ skip_bits_long(rw, 8 * payload_size);
+
+ if (!cbs_h2645_read_more_rbsp_data(rw))
+ break;
+ }
+#else
+ for (k = 0; k < current->nb_blocks; k++) {
+ PutBitContext start_state;
+ uint32_t tmp;
+ int trace, i;
+
+ block = ¤t->blocks[k];
+
+ // We write the payload twice in order to find the size. Trace
+ // output is switched off for the first write.
+ trace = ctx->trace_enable;
+ ctx->trace_enable = 0;
+
+ start_state = *rw;
+ for (i = 0; i < 2; i++) {
+ *rw = start_state;
+
+ tmp = FFMIN(block->payload_size, 7);
+ xu(3, payload_size_type, tmp, 0, 7, 0);
+ xu(5, payload_type, block->payload_type, 0, MAX_UINT_BITS(5), 0);
+
+ if (tmp == 7)
+ xmb(custom_byte_size, block->payload_size);
+
+ err = FUNC(process_block)(ctx, rw, block);
+ ctx->trace_enable = trace;
+ if (err < 0)
+ return err;
+ }
+ }
+#endif
+
+ return 0;
+}
+
+static int FUNC(nal)(CodedBitstreamContext *ctx, RWContext *rw,
+ LCEVCRawNAL *current, int nal_unit_type)
+{
+ int err;
+
+ if (nal_unit_type == LCEVC_NON_IDR_NUT)
+ HEADER("Non IDR");
+ else
+ HEADER("IDR");
+
+ CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header,
+ (1 << LCEVC_IDR_NUT) | (1 << LCEVC_NON_IDR_NUT)));
+
+ CHECK(FUNC(process_block_list) (ctx, rw, ¤t->process_block_list));
+
+ CHECK(FUNC(rbsp_trailing_bits)(ctx, rw));
+
+ return 0;
+}
diff --git a/libavformat/cbs.h b/libavformat/cbs.h
index e4dc231001..1de86a5fce 100644
--- a/libavformat/cbs.h
+++ b/libavformat/cbs.h
@@ -25,6 +25,7 @@
#define CBS_H264 0
#define CBS_H265 0
#define CBS_H266 0
+#define CBS_LCEVC 0
#define CBS_JPEG 0
#define CBS_MPEG2 0
#define CBS_VP8 0
--
2.52.0
_______________________________________________
ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org
reply other threads:[~2026-02-19 1:02 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
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=177146287775.25.119570817854103840@29965ddac10e \
--to=ffmpeg-devel@ffmpeg.org \
--cc=code@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