[FFmpeg-devel] [PATCH v6 4/9] avcodec: add cbs for h266/vvc
Mark Thompson
sw at jkqxz.net
Thu Feb 18 02:03:57 EET 2021
On 17/02/2021 01:51, Nuo Mi wrote:
> ---
> configure | 2 +
> libavcodec/Makefile | 1 +
> libavcodec/cbs.c | 6 +
> libavcodec/cbs_h2645.c | 541 ++++-
> libavcodec/cbs_h266.h | 817 +++++++
> libavcodec/cbs_h266_syntax_template.c | 3006 +++++++++++++++++++++++++
> libavcodec/cbs_internal.h | 3 +-
> libavcodec/cbs_sei.c | 29 +
> 8 files changed, 4397 insertions(+), 8 deletions(-)
> create mode 100644 libavcodec/cbs_h266.h
> create mode 100644 libavcodec/cbs_h266_syntax_template.c
>
> diff --git a/configure b/configure
> index a76c2ec4ae..11df59a229 100755
> --- a/configure
> +++ b/configure
> @@ -2355,6 +2355,7 @@ CONFIG_EXTRA="
> cbs_av1
> cbs_h264
> cbs_h265
> + cbs_h266
> cbs_jpeg
> cbs_mpeg2
> cbs_vp9
> @@ -2623,6 +2624,7 @@ threads_if_any="$THREADS_LIST"
> cbs_av1_select="cbs"
> cbs_h264_select="cbs"
> cbs_h265_select="cbs"
> +cbs_h266_select="cbs"
> cbs_jpeg_select="cbs"
> cbs_mpeg2_select="cbs"
> cbs_vp9_select="cbs"
> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> index 3341801b97..23553e68e9 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -73,6 +73,7 @@ OBJS-$(CONFIG_CBS) += cbs.o cbs_bsf.o
> OBJS-$(CONFIG_CBS_AV1) += cbs_av1.o
> OBJS-$(CONFIG_CBS_H264) += cbs_h2645.o cbs_sei.o h2645_parse.o
> OBJS-$(CONFIG_CBS_H265) += cbs_h2645.o cbs_sei.o h2645_parse.o
> +OBJS-$(CONFIG_CBS_H266) += cbs_h2645.o cbs_sei.o h2645_parse.o
> OBJS-$(CONFIG_CBS_JPEG) += cbs_jpeg.o
> OBJS-$(CONFIG_CBS_MPEG2) += cbs_mpeg2.o
> OBJS-$(CONFIG_CBS_VP9) += cbs_vp9.o
> diff --git a/libavcodec/cbs.c b/libavcodec/cbs.c
> index ecf22b3fdb..6baf21d62b 100644
> --- a/libavcodec/cbs.c
> +++ b/libavcodec/cbs.c
> @@ -39,6 +39,9 @@ static const CodedBitstreamType *const cbs_type_table[] = {
> #if CONFIG_CBS_H265
> &ff_cbs_type_h265,
> #endif
> +#if CONFIG_CBS_H266
> + &ff_cbs_type_h266,
> +#endif
> #if CONFIG_CBS_JPEG
> &ff_cbs_type_jpeg,
> #endif
> @@ -60,6 +63,9 @@ const enum AVCodecID ff_cbs_all_codec_ids[] = {
> #if CONFIG_CBS_H265
> AV_CODEC_ID_H265,
> #endif
> +#if CONFIG_CBS_H266
> + AV_CODEC_ID_H266,
> +#endif
> #if CONFIG_CBS_JPEG
> AV_CODEC_ID_MJPEG,
> #endif
> diff --git a/libavcodec/cbs_h2645.c b/libavcodec/cbs_h2645.c
> index 57c419aa05..d39464ba49 100644
> --- a/libavcodec/cbs_h2645.c
> +++ b/libavcodec/cbs_h2645.c
> @@ -24,12 +24,129 @@
> #include "cbs_internal.h"
> #include "cbs_h264.h"
> #include "cbs_h265.h"
> +#include "cbs_h266.h"
> #include "h264.h"
> #include "h264_sei.h"
> #include "h2645_parse.h"
> #include "hevc.h"
> #include "hevc_sei.h"
> +#include "vvc.h"
>
> +#define H266_IS_SLICE(nut) (nut <= VVC_RASL_NUT || (nut >= VVC_IDR_W_RADL && nut <= VVC_GDR_NUT))
> +#define H266_IS_IDR(nut) (nut == VVC_IDR_W_RADL || nut == VVC_IDR_N_LP)
> +
> +static void h266_au_detector_init(H266AuDetector* d)
> +{
> + if (!d->inited) {
> + d->prev_layer_id = UINT8_MAX;
> + d->prev_poc = INT_MAX;
> + d->prev_tid0_poc = INT_MAX;
> + d->inited = 1;
> + }
> +}
> +
> +//8.3.1 Decoding process for picture order count.
> +//VTM did not follow the spec, and it's much simpler than spec.
> +//We follow the VTM.
To clarify, does that mean that the VTM is wrong or that the spec defines it in an equivalent way which is overly complicated?
> +static int h266_get_slice_poc(CodedBitstreamH266Context *h266, void* log_ctx,
> + const H266RawPH *ph, const H266RawSliceHeader *slice,
> + int *poc)
> +{
> + int poc_msb, max_poc_lsb, poc_lsb;
> + H266AuDetector* d = &h266->priv.au_detector;
> + const H266RawSPS* sps;
> + const H266RawPPS* pps;
Please keep the '*'s as part of the declarator, not the declaration-specifiers (in lots of places in these functions).
> +
> + pps = h266->pps[ph->ph_pic_parameter_set_id];
> + if (!pps) {
> + av_log(log_ctx, AV_LOG_ERROR, "PPS id %d is not avaliable.\n",
> + ph->ph_pic_parameter_set_id);
> + return AVERROR_INVALIDDATA;
> + }
> +
> + sps = h266->sps[pps->pps_seq_parameter_set_id];
> + if (!sps) {
> + av_log(log_ctx, AV_LOG_ERROR, "SPS id %d is not avaliable.\n",
> + pps->pps_seq_parameter_set_id);
> + return AVERROR_INVALIDDATA;
> + }
> +
> + max_poc_lsb = 1 << (sps->sps_log2_max_pic_order_cnt_lsb_minus4 + 4);
> + poc_lsb = ph->ph_pic_order_cnt_lsb;
> + if (H266_IS_IDR(slice->nal_unit_header.nal_unit_type)) {
> + if (ph->ph_poc_msb_cycle_present_flag)
> + poc_msb = ph->ph_poc_msb_cycle_val * max_poc_lsb;
> + else
> + poc_msb = 0;
> + } else {
> + int prev_poc = d->prev_tid0_poc;
> + int prev_poc_lsb = prev_poc & (max_poc_lsb - 1);
> + int prev_poc_msb = prev_poc - prev_poc_lsb;
> + if (ph->ph_poc_msb_cycle_present_flag) {
> + poc_msb = ph->ph_poc_msb_cycle_val * max_poc_lsb;
> + } else {
> + if ((poc_lsb < prev_poc_lsb) && ((prev_poc_lsb - poc_lsb) >= (max_poc_lsb / 2)))
> + poc_msb = prev_poc_msb + max_poc_lsb;
> + else if ((poc_lsb > prev_poc_lsb) && ((poc_lsb - prev_poc_lsb) > (max_poc_lsb / 2)))
> + poc_msb = prev_poc_msb - max_poc_lsb;
> + else
> + poc_msb = prev_poc_msb;
> + }
> + }
> +
> + *poc = poc_msb + poc_lsb;
> + return 0;
> +}
> +
> +int h266_is_au_start(CodedBitstreamH266Context *h266, CodedBitstreamFragment *pu, void* log_ctx)
As an unexported global, this needs an "ff_" prefix.
> +{
> + //7.4.2.4.3
> + H266AuDetector *d = &h266->priv.au_detector;
> + int i, ret;
> + const H266RawNALUnitHeader *nal;
> + const H266RawPH *ph = h266->ph;
> + const H266RawSlice *slice = NULL;
> + int poc, nut;
> +
> + if (!ph) {
> + av_log(log_ctx, AV_LOG_ERROR, "can't find picture header in picutre unit.\n");
> + return AVERROR_INVALIDDATA;
> + }
> + for (i = 0; i < pu->nb_units; i++) {
> + nal = pu->units[i].content;
> + if (!nal)
> + continue;
> + if (H266_IS_SLICE(nal->nal_unit_type)) {
> + slice = pu->units[i].content;
> + break;
> + }
> + }
> + if (!slice) {
> + av_log(log_ctx, AV_LOG_ERROR, "can't find first slice.\n");
> + return AVERROR_INVALIDDATA;
> + }
> +
> + h266_au_detector_init(&h266->priv.au_detector);
> +
> + if ((ret = h266_get_slice_poc(h266, log_ctx, ph, &slice->header, &poc)) < 0)
> + return ret;
> +
> + ret = (nal->nuh_layer_id <= d->prev_layer_id) || (poc != d->prev_poc);
> +
> + nut = nal->nal_unit_type;
> + d->prev_layer_id = nal->nuh_layer_id;
> + d->prev_poc = poc;
> + if (nal->nuh_temporal_id_plus1 == 1 &&
> + !ph->ph_non_ref_pic_flag && nut != VVC_RADL_NUT && nut != VVC_RASL_NUT) {
> + d->prev_tid0_poc = poc;
> + }
> + return ret;
> +}
None of this logic is used by the syntax tables, so I think move it down below them.
> +
> +static av_always_inline unsigned int h266_ceil(unsigned int v, unsigned int align)
> +{
> + return (((v) + (align) - 1) / (align));
> +}
>
> static int cbs_read_ue_golomb(CodedBitstreamContext *ctx, GetBitContext *gbc,
> const char *name, const int *subscripts,
> ...
> @@ -515,6 +641,12 @@ static int cbs_h2645_split_fragment(CodedBitstreamContext *ctx,
> if (frag->data_size == 0)
> return 0;
>
> + if (codec_id == AV_CODEC_ID_VVC) {
> + //we deactive picture header here to avoid reuse previous frame's ph.
> + CodedBitstreamH266Context* h266 = ctx->priv_data;
> + h266->ph = NULL;
> + }
> +
> if (header && frag->data[0] && codec_id == AV_CODEC_ID_H264) {
> // AVCC header.
> size_t size, start, end;
> @@ -716,6 +848,12 @@ static int cbs_h2645_replace_ps(CodedBitstreamContext *ctx,
> H2645_PS_TYPE(H265, HEVC_NAL_ ## cname, cname, uname, \
> HEVC_MAX_ ## cname ## _COUNT, \
> uname ## _ ## id_name, active_ ## uname)
> +#define H266_PS_TYPE(cname, uname, id_name) \
> + H26456_PS_TYPE(H266, VVC_ ## cname ## _NUT, cname, uname, \
> + VVC_MAX_ ## cname ## _COUNT, \
> + offsetof(H266 ## Raw ## cname, uname ## _ ## id_name ## _parameter_set_id), \
> + 0)
As I said in the comment on the previous patch, offsetof(common) is zero so there is no need to mess around with adding more offsetof()s.
> +#define H266_PH_TYPE H26456_PS_TYPE(H266, VVC_PH_NUT, PH, ph, 1, 0, 0)
I'm not convinced that reusing the replace-PS function for the PH is making this nicer (including the name). Expanding the unref/ref inline looks simpler than adding extra special cases here.
>
> static const PSType ps_types[] = {
> H264_PS_TYPE(SPS, sps, seq),
> @@ -723,6 +861,10 @@ static int cbs_h2645_replace_ps(CodedBitstreamContext *ctx,
> H265_PS_TYPE(VPS, vps, video),
> H265_PS_TYPE(SPS, sps, seq),
> H265_PS_TYPE(PPS, pps, pic),
> + H266_PS_TYPE(VPS, vps, video),
> + H266_PS_TYPE(SPS, sps, seq),
> + H266_PS_TYPE(PPS, pps, pic),
> + H266_PH_TYPE,
> };
>
> const PSType *ps_type;
> @@ -1019,6 +1161,131 @@ static int cbs_h265_read_nal_unit(CodedBitstreamContext *ctx,
> return 0;
> }
>
> ...
> static int cbs_h2645_unit_requires_zero_byte(enum AVCodecID codec_id,
> CodedBitstreamUnitType type,
> - int nal_unit_index)
> + int nal_unit_index, int h266_au_start)
> {
> - // Section B.1.2 in H.264, section B.2.2 in H.265.
> + // Section B.1.2 in H.264, section B.2.2 in H.265, H.266.
> if (nal_unit_index == 0) {
> - // Assume that this is the first NAL unit in an access unit.
> - return 1;
> + if (codec_id == AV_CODEC_ID_VVC) {
> + if (h266_au_start)
> + return 1;
> + } else {
> + // Assume that this is the first NAL unit in an access unit.
> + return 1;
> + }
> }
> if (codec_id == AV_CODEC_ID_H264)
> return type == H264_NAL_SPS || type == H264_NAL_PPS;
> if (codec_id == AV_CODEC_ID_HEVC)
> return type == HEVC_NAL_VPS || type == HEVC_NAL_SPS || type == HEVC_NAL_PPS;
> + if (codec_id == AV_CODEC_ID_VVC)
> + return type >= VVC_OPI_NUT && type <= VVC_SUFFIX_APS_NUT; > return 0;
> }
>
> @@ -1327,7 +1717,7 @@ static int cbs_h2645_assemble_fragment(CodedBitstreamContext *ctx,
> {
> uint8_t *data;
> size_t max_size, dp, sp;
> - int err, i, zero_run;
> + int err, i, zero_run, h266_au_start = 0;
>
> for (i = 0; i < frag->nb_units; i++) {
> // Data should already all have been written when we get here.
> @@ -1343,7 +1733,10 @@ static int cbs_h2645_assemble_fragment(CodedBitstreamContext *ctx,
> data = av_realloc(NULL, max_size + AV_INPUT_BUFFER_PADDING_SIZE);
> if (!data)
> return AVERROR(ENOMEM);
> -
> + if (ctx->codec->codec_id == AV_CODEC_ID_VVC) {
> + CodedBitstreamH266Context *h266 = ctx->priv_data;
> + h266_au_start = h266_is_au_start(h266, frag, ctx->log_ctx) > 0;
> + }
It is unclear to me why this wants a special case.
The current logic is that we are writing an AU, so the first NAL unit in it is necessarily an AU start and subsequent NAL units are not?
> dp = 0;
> for (i = 0; i < frag->nb_units; i++) {
> CodedBitstreamUnit *unit = &frag->units[i];
> @@ -1356,7 +1749,7 @@ static int cbs_h2645_assemble_fragment(CodedBitstreamContext *ctx,
> frag->data_bit_padding = unit->data_bit_padding;
> }
>
> - if (cbs_h2645_unit_requires_zero_byte(ctx->codec->codec_id, unit->type, i)) {
> + if (cbs_h2645_unit_requires_zero_byte(ctx->codec->codec_id, unit->type, i, h266_au_start)) {
> // zero_byte
> data[dp++] = 0;
> }
> @@ -1470,6 +1863,37 @@ static void cbs_h265_close(CodedBitstreamContext *ctx)
> av_buffer_unref(&h265->pps_ref[i]);
> }
>
> ...
> diff --git a/libavcodec/cbs_h266.h b/libavcodec/cbs_h266.h
> new file mode 100644
> index 0000000000..20d547a26d
> --- /dev/null
> +++ b/libavcodec/cbs_h266.h
> @@ -0,0 +1,817 @@
> +/*
> + * 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_H266_H
> +#define AVCODEC_CBS_H266_H
> +
> +#include <stddef.h>
> +#include <stdint.h>
> +
> +#include "cbs_h2645.h"
> +#include "cbs_sei.h"
> +#include "vvc.h"
> +
> +enum {
> + // This limit is arbitrary - it is sufficient for one message of each
> + // type plus some repeats, and will therefore easily cover all sane
> + // streams. However, it is possible to make technically-valid streams
> + // for which it will fail (for example, by including a large number of
> + // user-data-unregistered messages).
> + VVC_MAX_SEI_PAYLOADS = 64,
Not used anywhere.
> +};
> +
> +typedef struct H266RawNALUnitHeader {
> + uint8_t nuh_layer_id;
> + uint8_t nal_unit_type;
> + uint8_t nuh_temporal_id_plus1;
> + uint8_t nuh_reserved_zero_bit;
> +} H266RawNALUnitHeader;
> +
> ...
> +
> +typedef struct H266RawSEIDecodedPictureHash {
> + uint8_t dph_sei_hash_type;
> + uint8_t dph_sei_single_component_flag;
> + uint8_t dph_sei_picture_md5[3][16];
> + uint16_t dph_sei_picture_crc[3];
> + uint32_t dph_sei_picture_checksum[3];
> +
> + uint8_t ph_sei_reserved_zero_7bits;
"dph"
> +} H266RawSEIDecodedPictureHash;
> +
> +typedef struct H266RawSEIPayload {
> + uint32_t payload_type;
> + uint32_t payload_size;
> + union {
> + H266RawSEIDecodedPictureHash decoded_picture_hash;
> + struct {
> + uint8_t *data;
> + AVBufferRef *data_ref;
> + size_t data_length;
> + } other;
> + } payload;
> + H266RawExtensionData extension_data;
> +} H266RawSEIPayload;
Not used.
> +
> +typedef struct H266RawSEI {
> + H266RawNALUnitHeader nal_unit_header;
> + SEIRawMessageList message_list;
> +} H266RawSEI;
> +
> +typedef struct H266AuDetector {
> + uint8_t inited;
> + uint8_t prev_layer_id;
> + int prev_tid0_poc;
> + int prev_poc;
> +} H266AuDetector;
> +
> +typedef struct CodedBitstreamH266Context {
> + // Reader/writer context in common with the H.264 implementation.
> + CodedBitstreamH2645Context common;
> +
> + // All currently available parameter sets. These are updated when
> + // any parameter set NAL unit is read/written with this context.
> + AVBufferRef *vps_ref[VVC_MAX_VPS_COUNT];
> + AVBufferRef *sps_ref[VVC_MAX_SPS_COUNT];
> + AVBufferRef *pps_ref[VVC_MAX_PPS_COUNT];
> + AVBufferRef *ph_ref;
> + H266RawVPS *vps[VVC_MAX_SPS_COUNT];
> + H266RawSPS *sps[VVC_MAX_SPS_COUNT];
> + H266RawPPS *pps[VVC_MAX_PPS_COUNT];
> + H266RawPH *ph;
> +
> + struct {
> + H266AuDetector au_detector;
> + } priv;
What is the extra struct layer doing? This is already the private context for an H.266 CBS instance.
> +
> +} CodedBitstreamH266Context;
> +
> ... > +
> +static int FUNC(ph)(CodedBitstreamContext *ctx, RWContext *rw, H266RawPH *current)
> +{
> + int err;
> +
> + HEADER("Picture Header");
> +
> + CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header, VVC_PH_NUT));
> + CHECK(FUNC(picture_header)(ctx, rw, current));
> + CHECK(FUNC(rbsp_trailing_bits)(ctx, rw));
> + return 0;
> +}
> +
> +static int FUNC(slice_header)(CodedBitstreamContext *ctx, RWContext *rw,
> + H266RawSliceHeader *current)
> +{
> + CodedBitstreamH266Context *h266 = ctx->priv_data;
> + const H266RawSPS *sps;
> + const H266RawPPS *pps;
> + const H266RawPH *ph;
> + const H266RefPicLists *ref_pic_lists;
> + int err, i;
> + uint8_t nal_unit_type, qp_bd_offset;
> + uint16_t curr_subpic_idx;
> + uint16_t num_slices_in_subpic;
> +
> + HEADER("Slice Header");
> +
> + CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header, -1));
> +
> + flag(sh_picture_header_in_slice_header_flag);
> + if (current->sh_picture_header_in_slice_header_flag){
> + CHECK(FUNC(picture_header)(ctx, rw, ¤t->sh_picture_header));
> + if (!h266->ph_ref) {
> + h266->ph_ref = av_buffer_allocz(sizeof(H266RawPH));
> + if (!h266->ph_ref)
> + return AVERROR(ENOMEM);
> + }
> + h266->ph = (H266RawPH*)h266->ph_ref->data;
> + memcpy(h266->ph, ¤t->sh_picture_header, sizeof(H266RawPH));
This memcpy() is naughty - if that was a ref to a previous picture header then you've just overwritten the reference while the other unit still holds it.
If the slice is unit->content then unit->content_ref is a reference to a superstructure of the picture header, avoiding the copy, but I'm not sure it's the right way to do it.
Is it right that the two possible options here are:
* Store a ref, which might be to a newly-allocated buffer or might be to a previous unit.
* Store the whole structure in the private context.
?
If the structure isn't too large, then the second option feels much simpler.
If it is large such that holding references and avoiding copies is beneficial (which is quite large, every ref entails a malloc/free call-pair), then we probably want that ref to always be to the unit?
> + }
> +
> + ph = h266->ph;
> + if (!ph) {
> + av_log(ctx->log_ctx, AV_LOG_ERROR, "Picture header not available.\n");
> + return AVERROR_INVALIDDATA;
> + }
> ...
> diff --git a/libavcodec/cbs_internal.h b/libavcodec/cbs_internal.h
> index a392880036..118b1052d4 100644
> --- a/libavcodec/cbs_internal.h
> +++ b/libavcodec/cbs_internal.h
> @@ -40,7 +40,7 @@ enum CBSContentType {
> enum {
> // Maximum number of unit types described by the same unit type
> // descriptor.
> - CBS_MAX_UNIT_TYPES = 3,
> + CBS_MAX_UNIT_TYPES = 4,
Heh, fair :)
> // Maximum number of reference buffer offsets in any one unit.
> CBS_MAX_REF_OFFSETS = 2,
> // Special value used in a unit type descriptor to indicate that it
> @@ -204,6 +204,7 @@ int ff_cbs_write_signed(CodedBitstreamContext *ctx, PutBitContext *pbc,
> extern const CodedBitstreamType ff_cbs_type_av1;
> extern const CodedBitstreamType ff_cbs_type_h264;
> extern const CodedBitstreamType ff_cbs_type_h265;
> +extern const CodedBitstreamType ff_cbs_type_h266;
> extern const CodedBitstreamType ff_cbs_type_jpeg;
> extern const CodedBitstreamType ff_cbs_type_mpeg2;
> extern const CodedBitstreamType ff_cbs_type_vp9;
> diff --git a/libavcodec/cbs_sei.c b/libavcodec/cbs_sei.c
> index c49830ad77..96c60259ce 100644
> --- a/libavcodec/cbs_sei.c
> +++ b/libavcodec/cbs_sei.c
> @@ -20,6 +20,7 @@
> #include "cbs_internal.h"
> #include "cbs_h264.h"
> #include "cbs_h265.h"
> +#include "cbs_h266.h"
> #include "cbs_sei.h"
>
> static void cbs_free_user_data_registered(void *opaque, uint8_t *data)
> @@ -132,6 +133,13 @@ static int cbs_sei_get_unit(CodedBitstreamContext *ctx,
> else
> sei_type = HEVC_NAL_SEI_SUFFIX;
> break;
> + case AV_CODEC_ID_H266:
> + highest_vcl_type = VVC_RSV_IRAP_11;
> + if (prefix)
> + sei_type = VVC_PREFIX_SEI_NUT;
> + else
> + sei_type = VVC_SUFFIX_SEI_NUT;
> + break;
> default:
> return AVERROR(EINVAL);
> }
> @@ -207,6 +215,18 @@ static int cbs_sei_get_unit(CodedBitstreamContext *ctx,
> memcpy(unit->content, &sei, sizeof(sei));
> }
> break;
> + case AV_CODEC_ID_H266:
> + {
> + H266RawSEI sei = {
> + .nal_unit_header = {
> + .nal_unit_type = sei_type,
> + .nuh_layer_id = 0,
> + .nuh_temporal_id_plus1 = 1,
> + },
> + };
> + memcpy(unit->content, &sei, sizeof(sei));
> + }
> + break;
> default:
> av_assert0(0);
> }
> @@ -237,6 +257,15 @@ static int cbs_sei_get_message_list(CodedBitstreamContext *ctx,
> *list = &sei->message_list;
> }
> break;
> + case AV_CODEC_ID_H266:
> + {
> + H266RawSEI *sei = unit->content;
> + if (unit->type != VVC_PREFIX_SEI_NUT &&
> + unit->type != VVC_SUFFIX_SEI_NUT)
> + return AVERROR(EINVAL);
> + *list = &sei->message_list;
> + }
> + break;
Yay, I'm glad the SEI stuff fitted in nicely with no extra weird cases.
> default:
> return AVERROR(EINVAL);
> }
>
Thanks,
- Mark
More information about the ffmpeg-devel
mailing list