[ffmpeg] branch master updated (4e32fb4c2a -> e245f4d5cf)
This is an automated email from the git hooks/post-receive script. Git pushed a change to branch master in repository ffmpeg. from 4e32fb4c2a fftools/cmdutils: constify string new 3dba9eb806 avcodec/bsf/extract_extradata: add support for LCEVC new 2d8c55b057 avcodec/h2645_parse: add support for LCEVC new 0390793dc3 avcodec/cbs: add support for LCEVC bitstreams new e245f4d5cf avcodec/bsf: add a LCEVC metadata bitstream filter The 4 revisions listed above as "new" are entirely new to this repository and will be described in separate emails. The revisions listed as "add" were already present in the repository and have only been added to this reference. Summary of changes: Changelog | 1 + configure | 3 + libavcodec/Makefile | 1 + libavcodec/bitstream_filters.c | 1 + libavcodec/bsf/Makefile | 1 + libavcodec/bsf/extract_extradata.c | 180 +++++++ libavcodec/bsf/lcevc_metadata.c | 204 ++++++++ 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 ++++++++++++++++++++++++++ libavcodec/h2645_parse.c | 63 +++ libavcodec/lcevc.h | 84 ++++ libavcodec/version.h | 4 +- libavformat/cbs.h | 1 + 16 files changed, 2366 insertions(+), 2 deletions(-) create mode 100644 libavcodec/bsf/lcevc_metadata.c create mode 100644 libavcodec/cbs_lcevc.c create mode 100644 libavcodec/cbs_lcevc.h create mode 100644 libavcodec/cbs_lcevc_syntax_template.c create mode 100644 libavcodec/lcevc.h
This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg. commit 3dba9eb80663abf77b083878a2f6e0f77abf81a7 Author: James Almer <jamrial@gmail.com> AuthorDate: Mon Feb 16 11:58:46 2026 -0300 Commit: James Almer <jamrial@gmail.com> CommitDate: Sat Feb 28 16:12:33 2026 -0300 avcodec/bsf/extract_extradata: add support for LCEVC Signed-off-by: James Almer <jamrial@gmail.com> --- libavcodec/bsf/extract_extradata.c | 180 +++++++++++++++++++++++++++++++++++++ libavcodec/lcevc.h | 84 +++++++++++++++++ 2 files changed, 264 insertions(+) diff --git a/libavcodec/bsf/extract_extradata.c b/libavcodec/bsf/extract_extradata.c index 43f4d62855..f7b62189fe 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; @@ -267,6 +277,174 @@ static int extract_extradata_h2645(AVBSFContext *ctx, AVPacket *pkt, return 0; } +static inline uint64_t get_mb(GetBitContext *s) { + int more, i = 0; + uint64_t mb = 0; + + do { + int byte = get_bits(s, 8); + unsigned bits = byte & 0x7f; + more = byte & 0x80; + mb = (mb << 7) | bits; + if (++i == 10) + break; + } while (more); + + return mb; +} + +/** + * Rewrite the NALu stripping the unneeded blocks. + * Given that length fields coded inside the NALu are not aware of any emulation_3bytes + * present in the bitstream, we need to keep track of the raw buffer as we navigate + * the stripped buffer. + */ +static int write_lcevc_nalu(AVBSFContext *ctx, PutByteContext *pbc, const H2645NAL *nal) +{ + GetByteContext gbc, raw_gbc; + int sc = 0, gc = 0; + int skipped_byte_pos = 0; + + bytestream2_init(&gbc, nal->data, nal->size); + bytestream2_init(&raw_gbc, nal->raw_data, nal->raw_size); + bytestream2_put_be16(pbc, bytestream2_get_be16(&gbc)); + bytestream2_skip(&raw_gbc, 2); + + while (bytestream2_get_bytes_left(&gbc) > 1) { + GetBitContext gb; + int payload_size_type, payload_type, payload_size; + int block_size, raw_block_size, block_end; + + init_get_bits8(&gb, gbc.buffer, bytestream2_get_bytes_left(&gbc)); + + payload_size_type = get_bits(&gb, 3); + payload_type = get_bits(&gb, 5); + payload_size = payload_size_type; + if (payload_size_type == 6) + return AVERROR_PATCHWELCOME; + if (payload_size_type == 7) + payload_size = get_mb(&gb); + + block_size = raw_block_size = payload_size + (get_bits_count(&gb) >> 3); + if (block_size >= bytestream2_get_bytes_left(&gbc)) + return AVERROR_INVALIDDATA; + + block_end = bytestream2_tell(&gbc) + block_size; + // Take into account removed emulation 3bytes, as payload_size in + // the bitstream is not aware of them. + for (; skipped_byte_pos < nal->skipped_bytes; skipped_byte_pos++) { + if (nal->skipped_bytes_pos[skipped_byte_pos] >= block_end) + break; + raw_block_size++; + } + + switch (payload_type) { + case 0: + bytestream2_put_buffer(pbc, raw_gbc.buffer, raw_block_size); + sc = 1; + break; + case 1: + bytestream2_put_buffer(pbc, raw_gbc.buffer, raw_block_size); + gc = 1; + break; + case 5: + bytestream2_put_buffer(pbc, raw_gbc.buffer, raw_block_size); + break; + default: + break; + } + + bytestream2_skip(&gbc, block_size); + bytestream2_skip(&raw_gbc, raw_block_size); + } + + if (!sc && !gc) + return AVERROR_INVALIDDATA; + + bytestream2_put_byte(pbc, 0x80); // rbsp_alignment bits + + return bytestream2_tell_p(pbc); +} + +static int extract_extradata_lcevc(AVBSFContext *ctx, AVPacket *pkt, + uint8_t **data, int *size) +{ + static const int extradata_nal_types[] = { + LCEVC_IDR_NUT, LCEVC_NON_IDR_NUT, + }; + + ExtractExtradataContext *s = ctx->priv_data; + PutByteContext pb_extradata; + int extradata_size = 0, filtered_size = 0; + size_t nb_extradata_nal_types = FF_ARRAY_ELEMS(extradata_nal_types); + int i, ret = 0; + + ret = ff_h2645_packet_split(&s->h2645_pkt, pkt->data, pkt->size, + ctx, 0, ctx->par_in->codec_id, H2645_FLAG_SMALL_PADDING); + if (ret < 0) + return ret; + + for (i = 0; i < s->h2645_pkt.nb_nals; i++) { + H2645NAL *nal = &s->h2645_pkt.nals[i]; + if (val_in_array(extradata_nal_types, nb_extradata_nal_types, nal->type)) { + bytestream2_init_writer(&pb_extradata, NULL, 0); + // dummy pass to find sc, gc or ai + if (!write_lcevc_nalu(ctx, &pb_extradata, nal)) + extradata_size += nal->raw_size + 3; + } else if (s->remove) { + filtered_size += nal->raw_size + 3; + } + } + + if (extradata_size) { + AVBufferRef *filtered_buf = NULL; + PutByteContext pb_filtered_data; + uint8_t *extradata; + + if (s->remove) { + filtered_buf = av_buffer_alloc(filtered_size + AV_INPUT_BUFFER_PADDING_SIZE); + if (!filtered_buf) { + return AVERROR(ENOMEM); + } + memset(filtered_buf->data + filtered_size, 0, AV_INPUT_BUFFER_PADDING_SIZE); + } + + extradata = av_malloc(extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); + if (!extradata) { + av_buffer_unref(&filtered_buf); + return AVERROR(ENOMEM); + } + + *data = extradata; + *size = extradata_size; + + bytestream2_init_writer(&pb_extradata, extradata, extradata_size); + if (s->remove) + bytestream2_init_writer(&pb_filtered_data, filtered_buf->data, filtered_size); + + for (i = 0; i < s->h2645_pkt.nb_nals; i++) { + H2645NAL *nal = &s->h2645_pkt.nals[i]; + if (val_in_array(extradata_nal_types, nb_extradata_nal_types, + nal->type)) { + bytestream2_put_be24(&pb_extradata, 1); //startcode + *size = write_lcevc_nalu(ctx, &pb_extradata, nal); + } else if (s->remove) { + bytestream2_put_be24(&pb_filtered_data, 1); //startcode + *size = write_lcevc_nalu(ctx, &pb_filtered_data, nal); + } + } + + if (s->remove) { + av_buffer_unref(&pkt->buf); + pkt->buf = filtered_buf; + pkt->data = filtered_buf->data; + pkt->size = filtered_size; + } + } + + return 0; +} + static int extract_extradata_vc1(AVBSFContext *ctx, AVPacket *pkt, uint8_t **data, int *size) { @@ -371,6 +549,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_lcevc }, { AV_CODEC_ID_MPEG1VIDEO, extract_extradata_mpeg12 }, { AV_CODEC_ID_MPEG2VIDEO, extract_extradata_mpeg12 }, { AV_CODEC_ID_MPEG4, extract_extradata_mpeg4 }, @@ -441,6 +620,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 */
This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg. commit 2d8c55b0578e726396230d40ebfedc4fa8f48bba Author: James Almer <jamrial@gmail.com> AuthorDate: Mon Feb 16 11:59:15 2026 -0300 Commit: James Almer <jamrial@gmail.com> CommitDate: Sat Feb 28 16:12:33 2026 -0300 avcodec/h2645_parse: add support for LCEVC Signed-off-by: James Almer <jamrial@gmail.com> --- libavcodec/h2645_parse.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/libavcodec/h2645_parse.c b/libavcodec/h2645_parse.c index fa57911c08..659e818393 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", // LCEVC_UNSPEC0_NUT + "UNSPEC1", // LCEVC_UNSPEC1_NUT + "UNSPEC2", // LCEVC_UNSPEC2_NUT + "UNSPEC3", // LCEVC_UNSPEC3_NUT + "UNSPEC4", // LCEVC_UNSPEC4_NUT + "UNSPEC5", // LCEVC_UNSPEC5_NUT + "UNSPEC6", // LCEVC_UNSPEC6_NUT + "UNSPEC7", // LCEVC_UNSPEC7_NUT + "UNSPEC8", // LCEVC_UNSPEC8_NUT + "UNSPEC9", // LCEVC_UNSPEC9_NUT + "UNSPEC10", // LCEVC_UNSPEC10_NUT + "UNSPEC11", // LCEVC_UNSPEC11_NUT + "UNSPEC12", // LCEVC_UNSPEC12_NUT + "UNSPEC13", // LCEVC_UNSPEC13_NUT + "UNSPEC14", // LCEVC_UNSPEC14_NUT + "UNSPEC15", // LCEVC_UNSPEC15_NUT + "UNSPEC16", // LCEVC_UNSPEC16_NUT + "UNSPEC17", // LCEVC_UNSPEC17_NUT + "UNSPEC18", // LCEVC_UNSPEC18_NUT + "UNSPEC19", // LCEVC_UNSPEC19_NUT + "UNSPEC20", // LCEVC_UNSPEC20_NUT + "UNSPEC21", // LCEVC_UNSPEC21_NUT + "UNSPEC22", // LCEVC_UNSPEC22_NUT + "UNSPEC23", // LCEVC_UNSPEC23_NUT + "UNSPEC24", // LCEVC_UNSPEC24_NUT + "UNSPEC25", // LCEVC_UNSPEC25_NUT + "UNSPEC26", // LCEVC_UNSPEC26_NUT + "UNSPEC27", // LCEVC_UNSPEC27_NUT + "NON_IDR_NUT", //LCEVC_NON_IDR_NUT + "IDR_NUT", // LCEVC_IDR_NUT + "RSV_NUT", // LCEVC_RSV_NUT + "UNSPEC31", // LCEVC_UNSPEC31_NUT +}; + +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)
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 <jamrial@gmail.com> AuthorDate: Wed Feb 18 21:15:17 2026 -0300 Commit: James Almer <jamrial@gmail.com> 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 <jamrial@gmail.com> --- 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
This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg. commit e245f4d5cf642faa6f43002654dfc84ba457b78c Author: James Almer <jamrial@gmail.com> AuthorDate: Thu Feb 19 10:40:48 2026 -0300 Commit: James Almer <jamrial@gmail.com> CommitDate: Sat Feb 28 16:14:40 2026 -0300 avcodec/bsf: add a LCEVC metadata bitstream filter Signed-off-by: James Almer <jamrial@gmail.com> --- Changelog | 1 + libavcodec/bitstream_filters.c | 1 + libavcodec/bsf/Makefile | 1 + libavcodec/bsf/lcevc_metadata.c | 204 ++++++++++++++++++++++++++++++++++++++++ libavcodec/version.h | 4 +- 5 files changed, 209 insertions(+), 2 deletions(-) diff --git a/Changelog b/Changelog index 26416cb1d6..f5ff8e549b 100644 --- a/Changelog +++ b/Changelog @@ -24,6 +24,7 @@ version <next>: - Remove the old HLS protocol handler - Vulkan compute codec optimizations - swscale Vulkan support +- LCEVC metadata bitstream filter version 8.0: diff --git a/libavcodec/bitstream_filters.c b/libavcodec/bitstream_filters.c index b4b852c7e6..150cca8939 100644 --- a/libavcodec/bitstream_filters.c +++ b/libavcodec/bitstream_filters.c @@ -48,6 +48,7 @@ extern const FFBitStreamFilter ff_hapqa_extract_bsf; extern const FFBitStreamFilter ff_hevc_metadata_bsf; extern const FFBitStreamFilter ff_hevc_mp4toannexb_bsf; extern const FFBitStreamFilter ff_imx_dump_header_bsf; +extern const FFBitStreamFilter ff_lcevc_metadata_bsf; extern const FFBitStreamFilter ff_media100_to_mjpegb_bsf; extern const FFBitStreamFilter ff_mjpeg2jpeg_bsf; extern const FFBitStreamFilter ff_mjpega_dump_header_bsf; diff --git a/libavcodec/bsf/Makefile b/libavcodec/bsf/Makefile index 360fd1e32f..eb090090ef 100644 --- a/libavcodec/bsf/Makefile +++ b/libavcodec/bsf/Makefile @@ -25,6 +25,7 @@ OBJS-$(CONFIG_HEVC_METADATA_BSF) += bsf/h265_metadata.o OBJS-$(CONFIG_DOVI_RPU_BSF) += bsf/dovi_rpu.o OBJS-$(CONFIG_HEVC_MP4TOANNEXB_BSF) += bsf/hevc_mp4toannexb.o OBJS-$(CONFIG_IMX_DUMP_HEADER_BSF) += bsf/imx_dump_header.o +OBJS-$(CONFIG_LCEVC_METADATA_BSF) += bsf/lcevc_metadata.o OBJS-$(CONFIG_MEDIA100_TO_MJPEGB_BSF) += bsf/media100_to_mjpegb.o OBJS-$(CONFIG_MJPEG2JPEG_BSF) += bsf/mjpeg2jpeg.o OBJS-$(CONFIG_MJPEGA_DUMP_HEADER_BSF) += bsf/mjpega_dump_header.o diff --git a/libavcodec/bsf/lcevc_metadata.c b/libavcodec/bsf/lcevc_metadata.c new file mode 100644 index 0000000000..4512999214 --- /dev/null +++ b/libavcodec/bsf/lcevc_metadata.c @@ -0,0 +1,204 @@ +/* + * 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/common.h" +#include "libavutil/opt.h" + +#include "bsf.h" +#include "bsf_internal.h" +#include "cbs.h" +#include "cbs_bsf.h" +#include "cbs_lcevc.h" +#include "lcevc.h" + +typedef struct LCEVCMetadataContext { + CBSBSFContext common; + + int overscan_appropriate_flag; + + int video_format; + int video_full_range_flag; + int colour_primaries; + int transfer_characteristics; + int matrix_coefficients; + + int chroma_sample_loc_type; + + LCEVCRawAdditionalInfo ai; + + int delete_filler; +} LCEVCMetadataContext; + +static int lcevc_metadata_handle_vui(AVBSFContext *bsf, + CodedBitstreamFragment *au) +{ + LCEVCMetadataContext *ctx = bsf->priv_data; + LCEVCRawProcessBlock *block = NULL; + LCEVCRawAdditionalInfo *ai = &ctx->ai; + LCEVCRawVUI *vui = &ai->vui; + int position, err; + + position = ff_cbs_lcevc_find_process_block(ctx->common.output, au, + LCEVC_PAYLOAD_TYPE_GLOBAL_CONFIG, + &block); + if (position < 0) + return 0; + + memset(ai, 0, sizeof(*ai)); + ai->additional_info_type = LCEVC_ADDITIONAL_INFO_TYPE_VUI; + + if (ctx->overscan_appropriate_flag >= 0) { + vui->overscan_info_present_flag = 1; + vui->overscan_appropriate_flag = ctx->overscan_appropriate_flag; + } + + if (ctx->video_format >= 0) { + vui->video_signal_type_present_flag = 1; + vui->video_format = ctx->video_format; + } else + vui->video_format = 5; + + if (ctx->video_full_range_flag >= 0) { + vui->video_signal_type_present_flag = 1; + vui->video_full_range_flag = ctx->video_full_range_flag; + } + + if (ctx->colour_primaries >= 0) { + vui->video_signal_type_present_flag = vui->colour_description_present_flag = 1; + vui->colour_primaries = ctx->colour_primaries; + } else + vui->colour_primaries = 2; + if (ctx->transfer_characteristics >= 0) { + vui->video_signal_type_present_flag = vui->colour_description_present_flag = 1; + vui->transfer_characteristics = ctx->transfer_characteristics; + } else + vui->transfer_characteristics = 2; + if (ctx->matrix_coefficients >= 0) { + vui->video_signal_type_present_flag = vui->colour_description_present_flag = 1; + vui->matrix_coefficients = ctx->matrix_coefficients; + } else + vui->matrix_coefficients = 2; + + if (ctx->chroma_sample_loc_type >= 0) { + vui->chroma_loc_info_present_flag = 1; + vui->chroma_sample_loc_type_top_field = ctx->chroma_sample_loc_type; + vui->chroma_sample_loc_type_top_field = ctx->chroma_sample_loc_type; + } + + err = ff_cbs_lcevc_add_process_block(ctx->common.output, au, position, + LCEVC_PAYLOAD_TYPE_ADDITIONAL_INFO, + ai, NULL); + if (err < 0) + return err; + + return 0; +} + +static int lcevc_metadata_update_fragment(AVBSFContext *bsf, AVPacket *pkt, + CodedBitstreamFragment *au) +{ + LCEVCMetadataContext *ctx = bsf->priv_data; + int err; + + if (ctx->overscan_appropriate_flag >= 0 || ctx->video_format >= 0 || + ctx->video_full_range_flag >= 0 || ctx->colour_primaries >= 0 || + ctx->transfer_characteristics >= 0 || ctx->matrix_coefficients >= 0 || + ctx->chroma_sample_loc_type >= 0) { + err = lcevc_metadata_handle_vui(bsf, au); + if (err < 0) + return err; + } + + if (ctx->delete_filler) { + 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) { + ff_cbs_lcevc_delete_process_block_type(ctx->common.output, au, + LCEVC_PAYLOAD_TYPE_FILLER); + } + } + } + + return 0; +} + +static const CBSBSFType lcevc_metadata_type = { + .codec_id = AV_CODEC_ID_LCEVC, + .fragment_name = "access unit", + .unit_name = "NAL unit", + .update_fragment = &lcevc_metadata_update_fragment, +}; + +static int lcevc_metadata_init(AVBSFContext *bsf) +{ + return ff_cbs_bsf_generic_init(bsf, &lcevc_metadata_type); +} + +#define OFFSET(x) offsetof(LCEVCMetadataContext, x) +#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_BSF_PARAM) +static const AVOption lcevc_metadata_options[] = { + { "overscan_appropriate_flag", "Set VUI overscan appropriate flag", + OFFSET(overscan_appropriate_flag), AV_OPT_TYPE_BOOL, + { .i64 = -1 }, -1, 1, FLAGS }, + + { "video_format", "Set video format (table E-2)", + OFFSET(video_format), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, 5, FLAGS}, + { "video_full_range_flag", "Set video full range flag", + OFFSET(video_full_range_flag), AV_OPT_TYPE_BOOL, + { .i64 = -1 }, -1, 1, FLAGS }, + { "colour_primaries", "Set colour primaries (table E-3)", + OFFSET(colour_primaries), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, 255, FLAGS }, + { "transfer_characteristics", "Set transfer characteristics (table E-4)", + OFFSET(transfer_characteristics), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, 255, FLAGS }, + { "matrix_coefficients", "Set matrix coefficients (table E-5)", + OFFSET(matrix_coefficients), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, 255, FLAGS }, + + { "chroma_sample_loc_type", "Set chroma sample location type (figure E-1)", + OFFSET(chroma_sample_loc_type), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, 5, FLAGS }, + + { "delete_filler", "Delete all filler", + OFFSET(delete_filler), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS}, + + { NULL } +}; + +static const AVClass lcevc_metadata_class = { + .class_name = "lcevc_metadata_bsf", + .item_name = av_default_item_name, + .option = lcevc_metadata_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static const enum AVCodecID lcevc_metadata_codec_ids[] = { + AV_CODEC_ID_LCEVC, AV_CODEC_ID_NONE, +}; + +const FFBitStreamFilter ff_lcevc_metadata_bsf = { + .p.name = "lcevc_metadata", + .p.codec_ids = lcevc_metadata_codec_ids, + .p.priv_class = &lcevc_metadata_class, + .priv_data_size = sizeof(LCEVCMetadataContext), + .init = &lcevc_metadata_init, + .close = &ff_cbs_bsf_generic_close, + .filter = &ff_cbs_bsf_generic_filter, +}; diff --git a/libavcodec/version.h b/libavcodec/version.h index 21443642a3..e0fe2eb7b8 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -29,8 +29,8 @@ #include "version_major.h" -#define LIBAVCODEC_VERSION_MINOR 23 -#define LIBAVCODEC_VERSION_MICRO 103 +#define LIBAVCODEC_VERSION_MINOR 24 +#define LIBAVCODEC_VERSION_MICRO 100 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ LIBAVCODEC_VERSION_MINOR, \
participants (2)
-
ffmpeg-git -
James Almer