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 <[email protected]> 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 <[email protected]> --- libavcodec/bsf/extract_extradata.c | 12 +++++ libavcodec/lcevc.h | 84 ++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 libavcodec/lcevc.h diff --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 <[email protected]> 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 <[email protected]> --- 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 <[email protected]> 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 <[email protected]> --- 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 -- [email protected] To unsubscribe send an email to [email protected]
