This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit 0390793dc36c1e390dd9559abd2e2502ebc7f92d Author: James Almer <[email protected]> AuthorDate: Wed Feb 18 21:15:17 2026 -0300 Commit: James Almer <[email protected]> CommitDate: Sat Feb 28 16:12:33 2026 -0300 avcodec/cbs: add support for LCEVC bitstreams As defined in ISO/IEC 23094-2:2021/FDAM 1:2023 Signed-off-by: James Almer <[email protected]> --- configure | 3 + libavcodec/Makefile | 1 + libavcodec/cbs.c | 6 + libavcodec/cbs_internal.h | 4 + libavcodec/cbs_lcevc.c | 833 +++++++++++++++++++++++++++++++++ libavcodec/cbs_lcevc.h | 308 ++++++++++++ libavcodec/cbs_lcevc_syntax_template.c | 674 ++++++++++++++++++++++++++ libavformat/cbs.h | 1 + 8 files changed, 1830 insertions(+) diff --git a/configure b/configure index 87a9c02686..16f82cdf07 100755 --- a/configure +++ b/configure @@ -2694,6 +2694,7 @@ CONFIG_EXTRA=" cbs_h265 cbs_h266 cbs_jpeg + cbs_lcevc cbs_mpeg2 cbs_vp8 cbs_vp9 @@ -3006,6 +3007,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" @@ -3692,6 +3694,7 @@ h264_metadata_bsf_deps="const_nan" h264_metadata_bsf_select="cbs_h264" h264_redundant_pps_bsf_select="cbs_h264" hevc_metadata_bsf_select="cbs_h265" +lcevc_metadata_bsf_select="cbs_lcevc" mjpeg2jpeg_bsf_select="jpegtables" mpeg2_metadata_bsf_select="cbs_mpeg2" smpte436m_to_eia608_bsf_select="smpte_436m" diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 7d3e4aa1b4..b33d7bcd0f 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -91,6 +91,7 @@ OBJS-$(CONFIG_CBS_AV1) += cbs_av1.o OBJS-$(CONFIG_CBS_H264) += cbs_h264.o cbs_h2645.o cbs_sei.o h2645_parse.o OBJS-$(CONFIG_CBS_H265) += cbs_h265.o cbs_h2645.o cbs_sei.o h2645_parse.o OBJS-$(CONFIG_CBS_H266) += cbs_h266.o cbs_h2645.o cbs_sei.o h2645_parse.o +OBJS-$(CONFIG_CBS_LCEVC) += cbs_lcevc.o cbs_h2645.o cbs_sei.o h2645_parse.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_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..11b47199b6 --- /dev/null +++ b/libavcodec/cbs_lcevc.c @@ -0,0 +1,833 @@ +/* + * 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 "libavutil/refstruct.h" +#include "bytestream.h" +#include "cbs.h" +#include "cbs_internal.h" +#include "cbs_h2645.h" +#include "cbs_lcevc.h" +#include "cbs_sei.h" +#include "get_bits.h" + +#define HEADER(name) do { \ + ff_cbs_trace_header(ctx, name); \ + } while (0) + +#define CHECK(call) do { \ + err = (call); \ + if (err < 0) \ + return err; \ + } while (0) + +#define FUNC_NAME2(rw, codec, name) cbs_ ## codec ## _ ## rw ## _ ## name +#define FUNC_NAME1(rw, codec, name) FUNC_NAME2(rw, codec, name) +#define FUNC_LCEVC(name) FUNC_NAME1(READWRITE, lcevc, name) +#define FUNC_NAME2_EXPORT(rw, codec, name) ff_cbs_ ## codec ## _ ## rw ## _ ## name +#define FUNC_NAME1_EXPORT(rw, codec, name) FUNC_NAME2_EXPORT(rw, codec, name) +#define FUNC_SEI(name) FUNC_NAME1_EXPORT(READWRITE, sei, name) + +#define SEI_FUNC(name, args) \ +static int FUNC_LCEVC(name) args; \ +static int FUNC_LCEVC(name ## _internal)(CodedBitstreamContext *ctx, \ + RWContext *rw, void *cur, \ + SEIMessageState *state) \ +{ \ + return FUNC_LCEVC(name)(ctx, rw, cur, state); \ +} \ +static int FUNC_LCEVC(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, \ + int nal_unit_type) \ +{ \ + return FUNC(name)(ctx, rw, cur, state, nal_unit_type); \ +} \ +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) \ + xu(width, name, current->name, range_min, range_max, 0, ) +#define flag(name) ub(1, name) +#define ue(name, range_min, range_max) \ + xue(name, current->name, range_min, range_max, 0, ) +#define i(width, name, range_min, range_max) \ + xi(width, name, current->name, range_min, range_max, 0, ) +#define ib(width, name) \ + xi(width, name, current->name, MIN_INT_BITS(width), MAX_INT_BITS(width), 0, ) +#define se(name, range_min, range_max) \ + xse(name, current->name, range_min, range_max, 0, ) + +#define us(width, name, range_min, range_max, subs, ...) \ + xu(width, name, current->name, range_min, range_max, subs, __VA_ARGS__) +#define ubs(width, name, subs, ...) \ + xu(width, name, current->name, 0, MAX_UINT_BITS(width), subs, __VA_ARGS__) +#define flags(name, subs, ...) \ + xu(1, name, current->name, 0, 1, subs, __VA_ARGS__) +#define ues(name, range_min, range_max, subs, ...) \ + xue(name, current->name, range_min, range_max, subs, __VA_ARGS__) +#define is(width, name, range_min, range_max, subs, ...) \ + xi(width, name, current->name, range_min, range_max, subs, __VA_ARGS__) +#define ibs(width, name, subs, ...) \ + 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; \ + xu(width, name, fixed_value, value, value, 0, ); \ + } while (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; +} + +#define READ +#define READWRITE read +#define RWContext GetBitContext + +#define ub(width, name) do { \ + uint32_t value; \ + CHECK(ff_cbs_read_simple_unsigned(ctx, rw, width, #name, \ + &value)); \ + current->name = value; \ + } while (0) +#define xu(width, name, var, range_min, range_max, subs, ...) do { \ + uint32_t value; \ + CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), \ + &value, range_min, range_max)); \ + var = value; \ + } while (0) +#define xue(name, var, range_min, range_max, subs, ...) do { \ + uint32_t value; \ + CHECK(ff_cbs_read_ue_golomb(ctx, rw, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), \ + &value, range_min, range_max)); \ + var = value; \ + } while (0) +#define xi(width, name, var, range_min, range_max, subs, ...) do { \ + int32_t value; \ + CHECK(ff_cbs_read_signed(ctx, rw, width, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), \ + &value, range_min, range_max)); \ + var = value; \ + } while (0) +#define xse(name, var, range_min, range_max, subs, ...) do { \ + int32_t value; \ + CHECK(ff_cbs_read_se_golomb(ctx, rw, #name, \ + SUBSCRIPTS(subs, __VA_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 { \ + current->name = value; \ + } while (0) + +#define more_rbsp_data(var) ((var) = ff_cbs_h2645_read_more_rbsp_data(rw)) + +#define bit_position(rw) (get_bits_count(rw)) +#define byte_alignment(rw) (get_bits_count(rw) % 8) + +/* The CBS LCEVC code uses the refstruct API for the allocation + * of its child buffers. */ +#define allocate(name, size) do { \ + name = av_refstruct_allocz(size + \ + AV_INPUT_BUFFER_PADDING_SIZE); \ + if (!name) \ + return AVERROR(ENOMEM); \ + } while (0) + +#define FUNC(name) FUNC_LCEVC(name) +#include "cbs_lcevc_syntax_template.c" +#undef FUNC + + +#undef READ +#undef READWRITE +#undef RWContext +#undef ub +#undef xu +#undef xi +#undef xue +#undef xse +#undef xmb +#undef infer +#undef more_rbsp_data +#undef bit_position +#undef byte_alignment +#undef allocate + + +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; +} + +#define WRITE +#define READWRITE write +#define RWContext PutBitContext + +#define ub(width, name) do { \ + uint32_t value = current->name; \ + CHECK(ff_cbs_write_simple_unsigned(ctx, rw, width, #name, \ + value)); \ + } while (0) +#define xu(width, name, var, range_min, range_max, subs, ...) do { \ + uint32_t value = var; \ + CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), \ + value, range_min, range_max)); \ + } while (0) +#define xue(name, var, range_min, range_max, subs, ...) do { \ + uint32_t value = var; \ + CHECK(ff_cbs_write_ue_golomb(ctx, rw, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), \ + value, range_min, range_max)); \ + } while (0) +#define xi(width, name, var, range_min, range_max, subs, ...) do { \ + int32_t value = var; \ + CHECK(ff_cbs_write_signed(ctx, rw, width, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), \ + value, range_min, range_max)); \ + } while (0) +#define xse(name, var, range_min, range_max, subs, ...) do { \ + int32_t value = var; \ + CHECK(ff_cbs_write_se_golomb(ctx, rw, #name, \ + 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)) { \ + av_log(ctx->log_ctx, AV_LOG_ERROR, \ + "%s does not match inferred value: " \ + "%"PRId64", but should be %"PRId64".\n", \ + #name, (int64_t)current->name, (int64_t)(value)); \ + return AVERROR_INVALIDDATA; \ + } \ + } while (0) + +#define more_rbsp_data(var) (var) + +#define bit_position(rw) (put_bits_count(rw)) +#define byte_alignment(rw) (put_bits_count(rw) % 8) + +#define allocate(name, size) do { \ + if (!name) { \ + av_log(ctx->log_ctx, AV_LOG_ERROR, "%s must be set " \ + "for writing.\n", #name); \ + return AVERROR_INVALIDDATA; \ + } \ + } while (0) + +#define FUNC(name) FUNC_LCEVC(name) +#include "cbs_lcevc_syntax_template.c" +#undef FUNC + +#undef WRITE +#undef READWRITE +#undef RWContext +#undef ub +#undef xu +#undef xi +#undef xue +#undef xse +#undef xmb +#undef u +#undef i +#undef flag +#undef ue +#undef se +#undef infer +#undef more_rbsp_data +#undef bit_position +#undef byte_alignment +#undef allocate + + +static int cbs_lcevc_split_fragment(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + int header) +{ + enum AVCodecID codec_id = ctx->codec->codec_id; + CodedBitstreamLCEVCContext *priv = ctx->priv_data; + CodedBitstreamH2645Context *h2645 = &priv->common; + GetByteContext gbc; + int err; + + av_assert0(frag->data && frag->nb_units == 0); + if (frag->data_size == 0) + return 0; + + if (header && frag->data[0]) { + // LVCC header. + size_t size, start, end; + int i, j, nb_arrays, nal_unit_type, nb_nals, version; + + h2645->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); + h2645->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(&h2645->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 = ff_cbs_h2645_fragment_add_nals(ctx, frag, &h2645->read_packet); + if (err < 0) + return err; + } + } else { + int flags = (H2645_FLAG_IS_NALFF * !!h2645->mp4) | H2645_FLAG_SMALL_PADDING | H2645_FLAG_USE_REF; + // Annex B, or later MP4 with already-known parameters. + + err = ff_h2645_packet_split(&h2645->read_packet, + frag->data, frag->data_size, + ctx->log_ctx, + h2645->nal_length_size, + codec_id, flags); + if (err < 0) + return err; + + err = ff_cbs_h2645_fragment_add_nals(ctx, frag, &h2645->read_packet); + if (err < 0) + return err; + } + + 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_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 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; + LCEVCRawSEI *sei = &additional_info->sei; + SEIRawMessage *message = &sei->message; + + av_refstruct_unref(&additional_info->payload_ref); + av_refstruct_unref(&sei->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, int position) +{ + LCEVCRawProcessBlock *blocks; + + if (position == -1) + position = list->nb_blocks; + av_assert0(position >= 0 && position <= list->nb_blocks); + + if (list->nb_blocks < list->nb_blocks_allocated) { + blocks = list->blocks; + + if (position < list->nb_blocks) + memmove(blocks + position + 1, blocks + position, + (list->nb_blocks - position) * sizeof(*blocks)); + } else { + blocks = av_malloc_array(list->nb_blocks*2 + 1, sizeof(*blocks)); + if (!blocks) + return AVERROR(ENOMEM); + + list->nb_blocks_allocated = 2*list->nb_blocks_allocated + 1; + + if (position > 0) + memcpy(blocks, list->blocks, position * sizeof(*blocks)); + + if (position < list->nb_blocks) + memcpy(blocks + position + 1, list->blocks + position, + (list->nb_blocks - position) * sizeof(*blocks)); + + av_free(list->blocks); + list->blocks = blocks; + } + + memset(blocks + position, 0, sizeof(*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_refstruct_unref(&block->extension_data); + } + av_free(list->blocks); +} + +static int cbs_lcevc_get_process_block_list(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit, + LCEVCRawProcessBlockList **list) +{ + LCEVCRawNAL *nal = unit->content; + if (unit->type != LCEVC_NON_IDR_NUT && unit->type != LCEVC_IDR_NUT) + return AVERROR(EINVAL); + *list = &nal->process_block_list; + + return 0; +} + +int ff_cbs_lcevc_add_process_block(CodedBitstreamContext *ctx, + CodedBitstreamFragment *au, + int position, + uint32_t payload_type, + void *payload_data, + void *payload_ref) +{ + const LCEVCProcessBlockTypeDescriptor *desc; + CodedBitstreamUnit *unit = NULL; + LCEVCRawProcessBlockList *list; + LCEVCRawProcessBlock *block; + int err; + + desc = ff_cbs_lcevc_process_block_find_type(ctx, payload_type); + if (!desc) + return AVERROR(EINVAL); + + for (int i = 0; i < au->nb_units; i++) { + if (au->units[i].type == LCEVC_NON_IDR_NUT || + au->units[i].type == LCEVC_IDR_NUT) { + unit = &au->units[i]; + break; + } + } + if (!unit) + return AVERROR(EINVAL); + + // Find the block list inside the codec-dependent unit. + err = cbs_lcevc_get_process_block_list(ctx, unit, &list); + if (err < 0) + return err; + + // Add a new block to the message list. + err = ff_cbs_lcevc_list_add(list, position); + if (err < 0) + return err; + + if (payload_ref) { + /* The following just increments payload_ref's refcount, + * so that payload_ref is now owned by us. */ + payload_ref = av_refstruct_ref(payload_ref); + } + + block = &list->blocks[position]; + + block->payload_type = payload_type; + block->payload = payload_data; + block->payload_ref = payload_ref; + + return 0; +} + +int ff_cbs_lcevc_find_process_block(CodedBitstreamContext *ctx, + CodedBitstreamFragment *au, + uint32_t payload_type, + LCEVCRawProcessBlock **iter) +{ + int err, found; + + found = 0; + for (int i = 0; i < au->nb_units; i++) { + CodedBitstreamUnit *unit = &au->units[i]; + LCEVCRawProcessBlockList *list; + + err = cbs_lcevc_get_process_block_list(ctx, unit, &list); + if (err < 0) + continue; + + for (int j = 0; j < list->nb_blocks; j++) { + LCEVCRawProcessBlock *block = &list->blocks[j]; + + if (block->payload_type == payload_type) { + if (!*iter || found) { + *iter = block; + return j; + } + if (block == *iter) + found = 1; + } + } + } + + return AVERROR(ENOENT); +} + +static void cbs_lcevc_delete_process_block(LCEVCRawProcessBlockList *list, + int position) +{ + LCEVCRawProcessBlock *block; + + av_assert0(0 <= position && position < list->nb_blocks); + + block = &list->blocks[position]; + av_refstruct_unref(&block->payload_ref); + + --list->nb_blocks; + + if (list->nb_blocks > 0) { + memmove(list->blocks + position, + list->blocks + position + 1, + (list->nb_blocks - position) * sizeof(*list->blocks)); + } +} + +void ff_cbs_lcevc_delete_process_block_type(CodedBitstreamContext *ctx, + CodedBitstreamFragment *au, + uint32_t payload_type) +{ + int err; + + for (int i = 0; i < au->nb_units; i++) { + CodedBitstreamUnit *unit = &au->units[i]; + LCEVCRawProcessBlockList *list; + + err = cbs_lcevc_get_process_block_list(ctx, unit, &list); + if (err < 0) + continue; + + for (int j = list->nb_blocks - 1; j >= 0; j--) { + if (list->blocks[j].payload_type == payload_type) + cbs_lcevc_delete_process_block(list, j); + } + } +} + +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_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 +}; + +// 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) +{ + for (int 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; +} + +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_lcevc_split_fragment, + .read_unit = &cbs_lcevc_read_nal_unit, + .write_unit = &cbs_lcevc_write_nal_unit, + .assemble_fragment = &ff_cbs_h2645_assemble_fragment, + + .flush = &cbs_lcevc_flush, + .close = &cbs_lcevc_close, +}; diff --git a/libavcodec/cbs_lcevc.h b/libavcodec/cbs_lcevc.h new file mode 100644 index 0000000000..371c7912e0 --- /dev/null +++ b/libavcodec/cbs_lcevc.h @@ -0,0 +1,308 @@ +/* + * 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 LCEVCRawSEI { + SEIRawMessage message; + + uint8_t payload_type; + uint32_t payload_size; + void *payload; + void *payload_ref; ///< RefStruct reference +} LCEVCRawSEI; + +typedef struct LCEVCRawAdditionalInfo { + uint8_t additional_info_type; + + LCEVCRawSEI sei; + LCEVCRawVUI vui; + + 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 + uint8_t *extension_data; ///< RefStruct reference + size_t extension_bit_length; +} 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; + // When writing, indicates that payload extension data is present so + // all extended fields must be written. May be updated by the writer + // to indicate that extended fields have been written, so the extension + // end bits must be written too. + uint8_t extension_present; +} LCEVCProcessBlockState; + +typedef int (*LCEVCRawProcessBlockReadFunction)(CodedBitstreamContext *ctx, + struct GetBitContext *rw, + void *current, + LCEVCProcessBlockState *state, + int nal_unit_type); + +typedef int (*LCEVCRawProcessBlockWriteFunction)(CodedBitstreamContext *ctx, + struct PutBitContext *rw, + void *current, + LCEVCProcessBlockState *state, + int nal_unit_type); + +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; + + int dithering_control_flag; + + // 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 at a given position. + */ +int ff_cbs_lcevc_list_add(LCEVCRawProcessBlockList *list, int position); + +/** + * Free all Process Block in a block list. + */ +void ff_cbs_lcevc_free_process_block_list(LCEVCRawProcessBlockList *list); + +/** + * Add a process block to an access unit. + * + * An existing NAL unit of type IDR or NON_IDR are required. + * + * If set, payload_ref must be a RefStruct reference backing payload_data. + * This function creates a new reference to payload_ref in this case. + * If payload_ref is NULL, the new message will not be reference counted. + */ +int ff_cbs_lcevc_add_process_block(CodedBitstreamContext *ctx, + CodedBitstreamFragment *au, + int position, + uint32_t payload_type, + void *payload_data, + void *payload_ref); + +/** + * Iterate over blocks with the given payload type in an access unit. + * + * Set block to NULL in the first call. Returns 0 while more blocks + * are available, AVERROR(ENOENT) when all blocks have been found. + */ +int ff_cbs_lcevc_find_process_block(CodedBitstreamContext *ctx, + CodedBitstreamFragment *au, + uint32_t payload_type, + LCEVCRawProcessBlock **block); + +/** + * Delete all blocks with the given payload type from an access unit. + */ +void ff_cbs_lcevc_delete_process_block_type(CodedBitstreamContext *ctx, + CodedBitstreamFragment *au, + uint32_t payload_type); + +#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..17ea907152 --- /dev/null +++ b/libavcodec/cbs_lcevc_syntax_template.c @@ -0,0 +1,674 @@ +/* + * 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, + int nal_unit_type)) +{ + 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, + int nal_unit_type)) +{ + 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, + int nal_unit_type)) +{ + 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); + priv->dithering_control_flag = current->dithering_control_flag; + infer(temporal_signalling_present_flag, priv->gc->temporal_enabled_flag && + !current->temporal_refresh_bit_flag); + } else { + infer(quant_matrix_mode, 0); + infer(dequant_offset_signalled_flag, 0); + ub(4, reserved_zeros_4bit); + flag(picture_type_bit_flag); + flag(temporal_refresh_bit_flag); + infer(step_width_sublayer1_enabled_flag, 0); + infer(dithering_control_flag, nal_unit_type == LCEVC_IDR_NUT + ? 0 + : priv->dithering_control_flag); + priv->dithering_control_flag = current->dithering_control_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); + } else + infer(level1_filtering_enabled_flag, 0); + + 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, + int nal_unit_type)) +{ + 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 (!ff_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; + skip_bits_long(rw, current->data_size * 8); +#else + err = ff_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, + LCEVCRawSEI *current, int payload_size) +{ + SEIRawMessage *message = ¤t->message; + int sei_type; + int i, err; + + ub(8, payload_type); + + if (current->payload_type == 1) + sei_type = SEI_TYPE_MASTERING_DISPLAY_COLOUR_VOLUME; + else if (current->payload_type == 2) + sei_type = SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO; + else if (current->payload_type == 4) + sei_type = SEI_TYPE_USER_DATA_REGISTERED_ITU_T_T35; + else if (current->payload_type == 5) + sei_type = SEI_TYPE_USER_DATA_UNREGISTERED; + else { + uint8_t *data; + +#ifdef READ + current->payload_size = payload_size; + 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, reserved_sei_message_payload_byte[i], data[i], 0, 255, 1, i); + + return 0; + } + + message->payload_type = sei_type; + message->payload_size = payload_size; + + CHECK(FUNC_SEI(message)(ctx, rw, message)); + + 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); + } + } else { + infer(aspect_ratio_idc, 0); + } + + flag(overscan_info_present_flag); + if (current->overscan_info_present_flag) + flag(overscan_appropriate_flag); + else + infer(overscan_appropriate_flag, 0); + + flag(video_signal_type_present_flag); + if (current->video_signal_type_present_flag) { + u(3, video_format, 0, 5); + 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); + } else { + infer(colour_primaries, 2); + infer(transfer_characteristics, 2); + infer(matrix_coefficients, 2); + } + } else { + infer(video_format, 5); + infer(video_full_range_flag, 0); + infer(colour_description_present_flag, 0); + infer(colour_primaries, 2); + infer(transfer_characteristics, 2); + infer(matrix_coefficients, 2); + } + 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); + } else { + infer(chroma_sample_loc_type_top_field, 0); + infer(chroma_sample_loc_type_bottom_field, 0); + } + + return 0; +} + +LCEVC_BLOCK_FUNC(additional_info, (CodedBitstreamContext *ctx, RWContext *rw, + LCEVCRawAdditionalInfo *current, + LCEVCProcessBlockState *state, + int nal_unit_type)) +{ + int i, err; + + HEADER("Additional Info"); + + ub(8, additional_info_type); + + if (current->additional_info_type == LCEVC_ADDITIONAL_INFO_TYPE_SEI) { + CHECK(FUNC(sei_payload)(ctx, rw, ¤t->sei, 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; + +#ifdef READ + current->payload_size = state->payload_size - 1; + 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, 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 nal_unit_type)) +{ + 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, + int nal_unit_type) +{ + 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, + .extension_present = current->extension_bit_length > 0, + }; + int start_position, current_position, bits_written; + +#ifdef READ + CHECK(ff_cbs_lcevc_alloc_process_block_payload(current, desc)); +#endif + + start_position = bit_position(rw); + + CHECK(desc->READWRITE(ctx, rw, current->payload, &state, nal_unit_type)); + + current_position = bit_position(rw); + bits_written = current_position - start_position; + + if (byte_alignment(rw) || state.extension_present || + bits_written < 8 * current->payload_size) { + size_t bits_left; + +#ifdef READ + GetBitContext tmp = *rw; + int trailing_bits, trailing_zero_bits; + + bits_left = 8 * current->payload_size - bits_written; + if (bits_left > 8) + skip_bits_long(&tmp, bits_left - 8); + trailing_bits = get_bits(&tmp, FFMIN(bits_left, 8)); + if (trailing_bits == 0) { + // The trailing bits must contain a payload_bit_equal_to_one, so + // they can't all be zero. + return AVERROR_INVALIDDATA; + } + trailing_zero_bits = ff_ctz(trailing_bits); + current->extension_bit_length = + bits_left - 1 - trailing_zero_bits; +#endif + + if (current->extension_bit_length > 0) { + allocate(current->extension_data, + (current->extension_bit_length + 7) / 8); + + bits_left = current->extension_bit_length; + for (i = 0; bits_left > 0; i++) { + int length = FFMIN(bits_left, 8); + xu(length, reserved_payload_extension_data, + current->extension_data[i], + 0, MAX_UINT_BITS(length), 0); + bits_left -= length; + } + } + + fixed(1, payload_bit_equal_to_one, 1); + while (byte_alignment(rw)) + fixed(1, payload_bit_equal_to_zero, 0); + } + +#ifdef WRITE + current->payload_size = (put_bits_count(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, + int nal_unit_type) +{ + 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, -1)); + block = ¤t->blocks[k]; + + block->payload_type = payload_type; + block->payload_size = payload_size; + + CHECK(FUNC(process_block)(ctx, &payload_gbc, block, nal_unit_type)); + + skip_bits_long(rw, 8 * payload_size); + + if (!ff_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, nal_unit_type); + 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, + current->nal_unit_header.nal_unit_type)); + + CHECK(FUNC(rbsp_trailing_bits)(ctx, rw)); + + return 0; +} diff --git a/libavformat/cbs.h b/libavformat/cbs.h index e4dc231001..dffd0e9206 100644 --- a/libavformat/cbs.h +++ b/libavformat/cbs.h @@ -26,6 +26,7 @@ #define CBS_H265 0 #define CBS_H266 0 #define CBS_JPEG 0 +#define CBS_LCEVC 0 #define CBS_MPEG2 0 #define CBS_VP8 0 #define CBS_VP9 0 _______________________________________________ ffmpeg-cvslog mailing list -- [email protected] To unsubscribe send an email to [email protected]
