diff mbox series

[FFmpeg-devel,03/27] cbs_h2645: Merge SEI message handling in common between codecs

Message ID 20210101213537.169546-4-sw@jkqxz.net
State Superseded
Headers show
Series Metadata handling in CBS
Related show

Checks

Context Check Description
andriy/x86_make success Make finished
andriy/x86_make_fate success Make fate finished
andriy/PPC64_make success Make finished
andriy/PPC64_make_fate success Make fate finished

Commit Message

Mark Thompson Jan. 1, 2021, 9:35 p.m. UTC
---
 libavcodec/Makefile                   |   4 +-
 libavcodec/cbs_h264.h                 |  50 +---
 libavcodec/cbs_h2645.c                | 300 +++++++++++----------
 libavcodec/cbs_h264_syntax_template.c | 173 +-----------
 libavcodec/cbs_h265.h                 |  33 +--
 libavcodec/cbs_h265_syntax_template.c | 269 +++----------------
 libavcodec/cbs_sei.c                  | 369 ++++++++++++++++++++++++++
 libavcodec/cbs_sei.h                  | 255 ++++++++++++++++++
 libavcodec/cbs_sei_syntax_template.c  | 215 +++++++++++++--
 libavcodec/h264_metadata_bsf.c        | 113 ++++----
 libavcodec/vaapi_encode_h264.c        |  51 ++--
 libavcodec/vaapi_encode_h265.c        |  38 +--
 12 files changed, 1107 insertions(+), 763 deletions(-)
 create mode 100644 libavcodec/cbs_sei.c

Comments

Nuo Mi Jan. 4, 2021, 3:42 p.m. UTC | #1
On Sat, Jan 2, 2021 at 5:44 AM Mark Thompson <sw@jkqxz.net> wrote:

> ---
>  libavcodec/Makefile                   |   4 +-
>  libavcodec/cbs_h264.h                 |  50 +---
>  libavcodec/cbs_h2645.c                | 300 +++++++++++----------
>  libavcodec/cbs_h264_syntax_template.c | 173 +-----------
>  libavcodec/cbs_h265.h                 |  33 +--
>  libavcodec/cbs_h265_syntax_template.c | 269 +++----------------
>  libavcodec/cbs_sei.c                  | 369 ++++++++++++++++++++++++++
>  libavcodec/cbs_sei.h                  | 255 ++++++++++++++++++
>  libavcodec/cbs_sei_syntax_template.c  | 215 +++++++++++++--
>  libavcodec/h264_metadata_bsf.c        | 113 ++++----
>  libavcodec/vaapi_encode_h264.c        |  51 ++--
>  libavcodec/vaapi_encode_h265.c        |  38 +--
>  12 files changed, 1107 insertions(+), 763 deletions(-)
>  create mode 100644 libavcodec/cbs_sei.c
>
> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> index 450781886d..6e12a8171d 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -71,8 +71,8 @@ OBJS-$(CONFIG_BSWAPDSP)                += bswapdsp.o
>  OBJS-$(CONFIG_CABAC)                   += cabac.o
>  OBJS-$(CONFIG_CBS)                     += cbs.o
>  OBJS-$(CONFIG_CBS_AV1)                 += cbs_av1.o
> -OBJS-$(CONFIG_CBS_H264)                += cbs_h2645.o h2645_parse.o
> -OBJS-$(CONFIG_CBS_H265)                += cbs_h2645.o h2645_parse.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_JPEG)                += cbs_jpeg.o
>  OBJS-$(CONFIG_CBS_MPEG2)               += cbs_mpeg2.o
>  OBJS-$(CONFIG_CBS_VP9)                 += cbs_vp9.o
> diff --git a/libavcodec/cbs_h264.h b/libavcodec/cbs_h264.h
> index 81113f1ad0..9eb97eae24 100644
> --- a/libavcodec/cbs_h264.h
> +++ b/libavcodec/cbs_h264.h
> @@ -291,34 +291,9 @@ typedef struct H264RawSEIDisplayOrientation {
>      uint8_t display_orientation_extension_flag;
>  } H264RawSEIDisplayOrientation;
>
> -typedef struct H264RawSEIPayload {
> -    uint32_t payload_type;
> -    uint32_t payload_size;
> -    union {
> -        H264RawSEIBufferingPeriod buffering_period;
> -        H264RawSEIPicTiming pic_timing;
> -        H264RawSEIPanScanRect pan_scan_rect;
> -        // H264RawSEIFiller filler -> no fields.
> -        SEIRawUserDataRegistered user_data_registered;
> -        SEIRawUserDataUnregistered user_data_unregistered;
> -        H264RawSEIRecoveryPoint recovery_point;
> -        H264RawSEIDisplayOrientation display_orientation;
> -        SEIRawMasteringDisplayColourVolume
> mastering_display_colour_volume;
> -        SEIRawAlternativeTransferCharacteristics
> -            alternative_transfer_characteristics;
> -        struct {
> -            uint8_t     *data;
> -            AVBufferRef *data_ref;
> -            size_t       data_length;
> -        } other;
> -    } payload;
> -} H264RawSEIPayload;
> -
>  typedef struct H264RawSEI {
>      H264RawNALUnitHeader nal_unit_header;
> -
> -    H264RawSEIPayload payload[H264_MAX_SEI_PAYLOADS];
> -    uint8_t payload_count;
> +    SEIRawMessageList    message_list;
>  } H264RawSEI;
>
>  typedef struct H264RawSliceHeader {
> @@ -438,27 +413,4 @@ typedef struct CodedBitstreamH264Context {
>      uint8_t last_slice_nal_unit_type;
>  } CodedBitstreamH264Context;
>
> -
> -/**
> - * Add an SEI message to an access unit.
> - *
> - * On success, the payload will be owned by a unit in access_unit;
> - * on failure, the content of the payload will be freed.
> - */
> -int ff_cbs_h264_add_sei_message(CodedBitstreamFragment *access_unit,
> -                                H264RawSEIPayload *payload);
> -
> -/**
> - * Delete an SEI message from an access unit.
> - *
> - * Deletes from nal_unit, which must be an SEI NAL unit.  If this is the
> - * last message in nal_unit, also deletes it from access_unit.
> - *
> - * Requires nal_unit to be a unit in access_unit and position to be >= 0
> - * and < the payload count of the SEI nal_unit.
> - */
> -void ff_cbs_h264_delete_sei_message(CodedBitstreamFragment *access_unit,
> -                                    CodedBitstreamUnit *nal_unit,
> -                                    int position);
> -
>  #endif /* AVCODEC_CBS_H264_H */
> diff --git a/libavcodec/cbs_h2645.c b/libavcodec/cbs_h2645.c
> index ce58e47a36..a00bc27370 100644
> --- a/libavcodec/cbs_h2645.c
> +++ b/libavcodec/cbs_h2645.c
> @@ -348,6 +348,7 @@ static int cbs_h2645_read_more_rbsp_data(GetBitContext
> *gbc)
>
>  #define more_rbsp_data(var) ((var) = cbs_h2645_read_more_rbsp_data(rw))
>
> +#define bit_position(rw)   (get_bits_count(rw))
>  #define byte_alignment(rw) (get_bits_count(rw) % 8)
>
>  #define allocate(name, size) do { \
> @@ -379,6 +380,7 @@ static int cbs_h2645_read_more_rbsp_data(GetBitContext
> *gbc)
>  #undef xse
>  #undef infer
>  #undef more_rbsp_data
> +#undef bit_position
>  #undef byte_alignment
>  #undef allocate
>
> @@ -424,6 +426,7 @@ static int cbs_h2645_read_more_rbsp_data(GetBitContext
> *gbc)
>
>  #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 { \
> @@ -460,6 +463,7 @@ static int cbs_h2645_read_more_rbsp_data(GetBitContext
> *gbc)
>  #undef se
>  #undef infer
>  #undef more_rbsp_data
> +#undef bit_position
>  #undef byte_alignment
>  #undef allocate
>
> @@ -1372,36 +1376,11 @@ static void cbs_h265_close(CodedBitstreamContext
> *ctx)
>          av_buffer_unref(&h265->pps_ref[i]);
>  }
>
> -static void cbs_h264_free_sei_payload(H264RawSEIPayload *payload)
> -{
> -    switch (payload->payload_type) {
> -    case H264_SEI_TYPE_BUFFERING_PERIOD:
> -    case H264_SEI_TYPE_PIC_TIMING:
> -    case H264_SEI_TYPE_PAN_SCAN_RECT:
> -    case H264_SEI_TYPE_RECOVERY_POINT:
> -    case H264_SEI_TYPE_DISPLAY_ORIENTATION:
> -    case H264_SEI_TYPE_MASTERING_DISPLAY_COLOUR_VOLUME:
> -    case H264_SEI_TYPE_ALTERNATIVE_TRANSFER:
> -        break;
> -    case H264_SEI_TYPE_USER_DATA_REGISTERED:
> -        av_buffer_unref(&payload->payload.user_data_registered.data_ref);
> -        break;
> -    case H264_SEI_TYPE_USER_DATA_UNREGISTERED:
> -
> av_buffer_unref(&payload->payload.user_data_unregistered.data_ref);
> -        break;
> -    default:
> -        av_buffer_unref(&payload->payload.other.data_ref);
> -        break;
> -    }
> -}
> -
>  static void cbs_h264_free_sei(void *opaque, uint8_t *content)
>  {
>      H264RawSEI *sei = (H264RawSEI*)content;
> -    int i;
> -    for (i = 0; i < sei->payload_count; i++)
> -        cbs_h264_free_sei_payload(&sei->payload[i]);
> -    av_freep(&content);
> +    ff_cbs_sei_free_message_list(&sei->message_list);
> +    av_free(content);
>  }
>
>  static const CodedBitstreamUnitTypeDescriptor cbs_h264_unit_types[] = {
> @@ -1433,42 +1412,11 @@ static const CodedBitstreamUnitTypeDescriptor
> cbs_h264_unit_types[] = {
>      CBS_UNIT_TYPE_END_OF_LIST
>  };
>
> -static void cbs_h265_free_sei_payload(H265RawSEIPayload *payload)
> -{
> -    switch (payload->payload_type) {
> -    case HEVC_SEI_TYPE_BUFFERING_PERIOD:
> -    case HEVC_SEI_TYPE_PICTURE_TIMING:
> -    case HEVC_SEI_TYPE_PAN_SCAN_RECT:
> -    case HEVC_SEI_TYPE_RECOVERY_POINT:
> -    case HEVC_SEI_TYPE_DISPLAY_ORIENTATION:
> -    case HEVC_SEI_TYPE_ACTIVE_PARAMETER_SETS:
> -    case HEVC_SEI_TYPE_DECODED_PICTURE_HASH:
> -    case HEVC_SEI_TYPE_TIME_CODE:
> -    case HEVC_SEI_TYPE_MASTERING_DISPLAY_INFO:
> -    case HEVC_SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO:
> -    case HEVC_SEI_TYPE_ALTERNATIVE_TRANSFER_CHARACTERISTICS:
> -    case HEVC_SEI_TYPE_ALPHA_CHANNEL_INFO:
> -        break;
> -    case HEVC_SEI_TYPE_USER_DATA_REGISTERED_ITU_T_T35:
> -        av_buffer_unref(&payload->payload.user_data_registered.data_ref);
> -        break;
> -    case HEVC_SEI_TYPE_USER_DATA_UNREGISTERED:
> -
> av_buffer_unref(&payload->payload.user_data_unregistered.data_ref);
> -        break;
> -    default:
> -        av_buffer_unref(&payload->payload.other.data_ref);
> -        break;
> -    }
> -    av_buffer_unref(&payload->extension_data.data_ref);
> -}
> -
>  static void cbs_h265_free_sei(void *opaque, uint8_t *content)
>  {
>      H265RawSEI *sei = (H265RawSEI*)content;
> -    int i;
> -    for (i = 0; i < sei->payload_count; i++)
> -        cbs_h265_free_sei_payload(&sei->payload[i]);
> -    av_freep(&content);
> +    ff_cbs_sei_free_message_list(&sei->message_list);
> +    av_free(content);
>  }
>
>  static const CodedBitstreamUnitTypeDescriptor cbs_h265_unit_types[] = {
> @@ -1548,92 +1496,164 @@ const CodedBitstreamType ff_cbs_type_h265 = {
>      .close             = &cbs_h265_close,
>  };
>
> -int ff_cbs_h264_add_sei_message(CodedBitstreamFragment *au,
> -                                H264RawSEIPayload *payload)
> -{
> -    H264RawSEI *sei = NULL;
> -    int err, i;
> -
> -    // Find an existing SEI NAL unit to add to.
> -    for (i = 0; i < au->nb_units; i++) {
> -        if (au->units[i].type == H264_NAL_SEI) {
> -            sei = au->units[i].content;
> -            if (sei->payload_count < H264_MAX_SEI_PAYLOADS)
> -                break;
> -
> -            sei = NULL;
> -        }
> -    }
> -
> -    if (!sei) {
> -        // Need to make a new SEI NAL unit.  Insert it before the first
> -        // slice data NAL unit; if no slice data, add at the end.
> -        AVBufferRef *sei_ref;
> -
> -        sei = av_mallocz(sizeof(*sei));
> -        if (!sei) {
> -            err = AVERROR(ENOMEM);
> -            goto fail;
> -        }
> -
> -        sei->nal_unit_header.nal_unit_type = H264_NAL_SEI;
> -        sei->nal_unit_header.nal_ref_idc   = 0;
> -
> -        sei_ref = av_buffer_create((uint8_t*)sei, sizeof(*sei),
> -                                   &cbs_h264_free_sei, NULL, 0);
> -        if (!sei_ref) {
> -            av_freep(&sei);
> -            err = AVERROR(ENOMEM);
> -            goto fail;
> -        }
> -
> -        for (i = 0; i < au->nb_units; i++) {
> -            if (au->units[i].type == H264_NAL_SLICE ||
> -                au->units[i].type == H264_NAL_IDR_SLICE)
> -                break;
> -        }
> -
> -        err = ff_cbs_insert_unit_content(au, i, H264_NAL_SEI,
> -                                         sei, sei_ref);
> -        av_buffer_unref(&sei_ref);
> -        if (err < 0)
> -            goto fail;
> -    }
> +static const SEIMessageTypeDescriptor cbs_sei_common_types[] = {
> +    {
> +        SEI_TYPE_FILLER_PAYLOAD,
> +        1, 1,
> +        sizeof(SEIRawFillerPayload),
> +        SEI_MESSAGE_RW(sei, filler_payload),
> +    },
> +    {
> +        SEI_TYPE_USER_DATA_REGISTERED_ITU_T_T35,
> +        1, 1,
> +        sizeof(SEIRawUserDataRegistered),
> +        SEI_MESSAGE_RW(sei, user_data_registered),
> +    },
> +    {
> +        SEI_TYPE_USER_DATA_UNREGISTERED,
> +        1, 1,
> +        sizeof(SEIRawUserDataUnregistered),
> +        SEI_MESSAGE_RW(sei, user_data_unregistered),
> +    },
> +    {
> +        SEI_TYPE_MASTERING_DISPLAY_COLOUR_VOLUME,
> +        1, 0,
> +        sizeof(SEIRawMasteringDisplayColourVolume),
> +        SEI_MESSAGE_RW(sei, mastering_display_colour_volume),
> +    },
> +    {
> +        SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO,
> +        1, 0,
> +        sizeof(SEIRawContentLightLevelInfo),
> +        SEI_MESSAGE_RW(sei, content_light_level_info),
> +    },
> +    {
> +        SEI_TYPE_ALTERNATIVE_TRANSFER_CHARACTERISTICS,
> +        1, 0,
> +        sizeof(SEIRawAlternativeTransferCharacteristics),
> +        SEI_MESSAGE_RW(sei, alternative_transfer_characteristics),
> +    },
> +    SEI_MESSAGE_TYPE_END,
> +};
>
> -    memcpy(&sei->payload[sei->payload_count], payload, sizeof(*payload));
> -    ++sei->payload_count;
> +static const SEIMessageTypeDescriptor cbs_sei_h264_types[] = {
> +    {
> +        SEI_TYPE_BUFFERING_PERIOD,
> +        1, 0,
> +        sizeof(H264RawSEIBufferingPeriod),
> +        SEI_MESSAGE_RW(h264, sei_buffering_period),
> +    },
> +    {
> +        SEI_TYPE_PIC_TIMING,
> +        1, 0,
> +        sizeof(H264RawSEIPicTiming),
> +        SEI_MESSAGE_RW(h264, sei_pic_timing),
> +    },
> +    {
> +        SEI_TYPE_PAN_SCAN_RECT,
> +        1, 0,
> +        sizeof(H264RawSEIPanScanRect),
> +        SEI_MESSAGE_RW(h264, sei_pan_scan_rect),
> +    },
> +    {
> +        SEI_TYPE_RECOVERY_POINT,
> +        1, 0,
> +        sizeof(H264RawSEIRecoveryPoint),
> +        SEI_MESSAGE_RW(h264, sei_recovery_point),
> +    },
> +    {
> +        SEI_TYPE_DISPLAY_ORIENTATION,
> +        1, 0,
> +        sizeof(H264RawSEIDisplayOrientation),
> +        SEI_MESSAGE_RW(h264, sei_display_orientation),
> +    },
> +    SEI_MESSAGE_TYPE_END
> +};
>
> -    return 0;
> -fail:
> -    cbs_h264_free_sei_payload(payload);
> -    return err;
> -}
> +static const SEIMessageTypeDescriptor cbs_sei_h265_types[] = {
> +    {
> +        SEI_TYPE_BUFFERING_PERIOD,
> +        1, 0,
> +        sizeof(H265RawSEIBufferingPeriod),
> +        SEI_MESSAGE_RW(h265, sei_buffering_period),
> +    },
> +    {
> +        SEI_TYPE_PIC_TIMING,
> +        1, 0,
> +        sizeof(H265RawSEIPicTiming),
> +        SEI_MESSAGE_RW(h265, sei_pic_timing),
> +    },
> +    {
> +        SEI_TYPE_PAN_SCAN_RECT,
> +        1, 0,
> +        sizeof(H265RawSEIPanScanRect),
> +        SEI_MESSAGE_RW(h265, sei_pan_scan_rect),
> +    },
> +    {
> +        SEI_TYPE_RECOVERY_POINT,
> +        1, 0,
> +        sizeof(H265RawSEIRecoveryPoint),
> +        SEI_MESSAGE_RW(h265, sei_recovery_point),
> +    },
> +    {
> +        SEI_TYPE_DISPLAY_ORIENTATION,
> +        1, 0,
> +        sizeof(H265RawSEIDisplayOrientation),
> +        SEI_MESSAGE_RW(h265, sei_display_orientation),
> +    },
> +    {
> +        SEI_TYPE_ACTIVE_PARAMETER_SETS,
> +        1, 0,
> +        sizeof(H265RawSEIActiveParameterSets),
> +        SEI_MESSAGE_RW(h265, sei_active_parameter_sets),
> +    },
> +    {
> +        SEI_TYPE_DECODED_PICTURE_HASH,
> +        0, 1,
> +        sizeof(H265RawSEIDecodedPictureHash),
> +        SEI_MESSAGE_RW(h265, sei_decoded_picture_hash),
> +    },
> +    {
> +        SEI_TYPE_TIME_CODE,
> +        1, 0,
> +        sizeof(H265RawSEITimeCode),
> +        SEI_MESSAGE_RW(h265, sei_time_code),
> +    },
> +    {
> +        SEI_TYPE_ALPHA_CHANNEL_INFO,
> +        1, 0,
> +        sizeof(H265RawSEIAlphaChannelInfo),
> +        SEI_MESSAGE_RW(h265, sei_alpha_channel_info),
> +    },
> +    SEI_MESSAGE_TYPE_END
> +};
>
> -void ff_cbs_h264_delete_sei_message(CodedBitstreamFragment *au,
> -                                    CodedBitstreamUnit *nal,
> -                                    int position)
> +const SEIMessageTypeDescriptor
> *ff_cbs_sei_find_type(CodedBitstreamContext *ctx,
> +                                                     int payload_type)
>  {
> -    H264RawSEI *sei = nal->content;
> -
> -    av_assert0(nal->type == H264_NAL_SEI);
> -    av_assert0(position >= 0 && position < sei->payload_count);
> -
> -    if (position == 0 && sei->payload_count == 1) {
> -        // Deleting NAL unit entirely.
> -        int i;
> +    const SEIMessageTypeDescriptor *codec_list;
> +    int i;
>
> -        for (i = 0; i < au->nb_units; i++) {
> -            if (&au->units[i] == nal)
> -                break;
> -        }
> +    for (i = 0; cbs_sei_common_types[i].type >= 0; i++) {
> +        if (cbs_sei_common_types[i].type == payload_type)
> +            return &cbs_sei_common_types[i];
> +    }
>
> -        ff_cbs_delete_unit(au, i);
> -    } else {
> -        cbs_h264_free_sei_payload(&sei->payload[position]);
> +    switch (ctx->codec->codec_id) {
> +    case AV_CODEC_ID_H264:
> +        codec_list = cbs_sei_h264_types;
> +        break;
> +    case AV_CODEC_ID_H265:
> +        codec_list = cbs_sei_h265_types;
> +        break;
> +    default:
> +        return NULL;
> +    }
>
> -        --sei->payload_count;
> -        memmove(sei->payload + position,
> -                sei->payload + position + 1,
> -                (sei->payload_count - position) * sizeof(*sei->payload));
> +    for (i = 0; codec_list[i].type >= 0; i++) {
> +        if (codec_list[i].type == payload_type)
> +            return &codec_list[i];
>      }
> +
> +    return NULL;
>  }
> diff --git a/libavcodec/cbs_h264_syntax_template.c
> b/libavcodec/cbs_h264_syntax_template.c
> index 76ed51cc7b..9587f33985 100644
> --- a/libavcodec/cbs_h264_syntax_template.c
> +++ b/libavcodec/cbs_h264_syntax_template.c
> @@ -511,7 +511,8 @@ static int FUNC(pps)(CodedBitstreamContext *ctx,
> RWContext *rw,
>  }
>
>  static int FUNC(sei_buffering_period)(CodedBitstreamContext *ctx,
> RWContext *rw,
> -                                      H264RawSEIBufferingPeriod *current)
> +                                      H264RawSEIBufferingPeriod *current,
> +                                      SEIMessageState *sei)
>  {
>      CodedBitstreamH264Context *h264 = ctx->priv_data;
>      const H264RawSPS *sps;
> @@ -604,7 +605,8 @@ static int
> FUNC(sei_pic_timestamp)(CodedBitstreamContext *ctx, RWContext *rw,
>  }
>
>  static int FUNC(sei_pic_timing)(CodedBitstreamContext *ctx, RWContext *rw,
> -                                H264RawSEIPicTiming *current)
> +                                H264RawSEIPicTiming *current,
> +                                SEIMessageState *sei)
>  {
>      CodedBitstreamH264Context *h264 = ctx->priv_data;
>      const H264RawSPS *sps;
> @@ -675,7 +677,8 @@ static int FUNC(sei_pic_timing)(CodedBitstreamContext
> *ctx, RWContext *rw,
>  }
>
>  static int FUNC(sei_pan_scan_rect)(CodedBitstreamContext *ctx, RWContext
> *rw,
> -                                   H264RawSEIPanScanRect *current)
> +                                   H264RawSEIPanScanRect *current,
> +                                   SEIMessageState *sei)
>  {
>      int err, i;
>
> @@ -701,7 +704,8 @@ static int
> FUNC(sei_pan_scan_rect)(CodedBitstreamContext *ctx, RWContext *rw,
>  }
>
>  static int FUNC(sei_recovery_point)(CodedBitstreamContext *ctx, RWContext
> *rw,
> -                                    H264RawSEIRecoveryPoint *current)
> +                                    H264RawSEIRecoveryPoint *current,
> +                                    SEIMessageState *sei)
>  {
>      int err;
>
> @@ -716,7 +720,8 @@ static int
> FUNC(sei_recovery_point)(CodedBitstreamContext *ctx, RWContext *rw,
>  }
>
>  static int FUNC(sei_display_orientation)(CodedBitstreamContext *ctx,
> RWContext *rw,
> -                                         H264RawSEIDisplayOrientation
> *current)
> +                                         H264RawSEIDisplayOrientation
> *current,
> +                                         SEIMessageState *sei)
>  {
>      int err;
>
> @@ -734,171 +739,17 @@ static int
> FUNC(sei_display_orientation)(CodedBitstreamContext *ctx, RWContext *
>      return 0;
>  }
>
> -static int FUNC(sei_payload)(CodedBitstreamContext *ctx, RWContext *rw,
> -                             H264RawSEIPayload *current)
> -{
> -    int err, i;
> -    int start_position, end_position;
> -
> -#ifdef READ
> -    start_position = get_bits_count(rw);
> -#else
> -    start_position = put_bits_count(rw);
> -#endif
> -
> -    switch (current->payload_type) {
> -    case H264_SEI_TYPE_BUFFERING_PERIOD:
> -        CHECK(FUNC(sei_buffering_period)
> -              (ctx, rw, &current->payload.buffering_period));
> -        break;
> -    case H264_SEI_TYPE_PIC_TIMING:
> -        CHECK(FUNC(sei_pic_timing)
> -              (ctx, rw, &current->payload.pic_timing));
> -        break;
> -    case H264_SEI_TYPE_PAN_SCAN_RECT:
> -        CHECK(FUNC(sei_pan_scan_rect)
> -              (ctx, rw, &current->payload.pan_scan_rect));
> -        break;
> -    case H264_SEI_TYPE_FILLER_PAYLOAD:
> -        {
> -            for (i = 0; i  < current->payload_size; i++)
> -                fixed(8, ff_byte, 0xff);
> -        }
> -        break;
> -    case H264_SEI_TYPE_USER_DATA_REGISTERED:
> -        CHECK(FUNC_SEI(sei_user_data_registered)
> -              (ctx, rw, &current->payload.user_data_registered,
> &current->payload_size));
> -        break;
> -    case H264_SEI_TYPE_USER_DATA_UNREGISTERED:
> -        CHECK(FUNC_SEI(sei_user_data_unregistered)
> -              (ctx, rw, &current->payload.user_data_unregistered,
> &current->payload_size));
> -        break;
> -    case H264_SEI_TYPE_RECOVERY_POINT:
> -        CHECK(FUNC(sei_recovery_point)
> -              (ctx, rw, &current->payload.recovery_point));
> -        break;
> -    case H264_SEI_TYPE_DISPLAY_ORIENTATION:
> -        CHECK(FUNC(sei_display_orientation)
> -              (ctx, rw, &current->payload.display_orientation));
> -        break;
> -    case H264_SEI_TYPE_MASTERING_DISPLAY_COLOUR_VOLUME:
> -        CHECK(FUNC_SEI(sei_mastering_display_colour_volume)
> -              (ctx, rw,
> &current->payload.mastering_display_colour_volume));
> -        break;
> -    case H264_SEI_TYPE_ALTERNATIVE_TRANSFER:
> -        CHECK(FUNC_SEI(sei_alternative_transfer_characteristics)
> -              (ctx, rw,
> &current->payload.alternative_transfer_characteristics));
> -        break;
> -    default:
> -        {
> -#ifdef READ
> -            current->payload.other.data_length = current->payload_size;
> -#endif
> -            allocate(current->payload.other.data,
> current->payload.other.data_length);
> -            for (i = 0; i < current->payload.other.data_length; i++)
> -                xu(8, payload_byte[i], current->payload.other.data[i], 0,
> 255, 1, i);
> -        }
> -    }
> -
> -    if (byte_alignment(rw)) {
> -        fixed(1, bit_equal_to_one, 1);
> -        while (byte_alignment(rw))
> -            fixed(1, bit_equal_to_zero, 0);
> -    }
> -
> -#ifdef READ
> -    end_position = get_bits_count(rw);
> -    if (end_position < start_position + 8 * current->payload_size) {
> -        av_log(ctx->log_ctx, AV_LOG_ERROR, "Incorrect SEI payload length:
> "
> -               "header %"PRIu32" bits, actually %d bits.\n",
> -               8 * current->payload_size,
> -               end_position - start_position);
> -        return AVERROR_INVALIDDATA;
> -    }
> -#else
> -    end_position = put_bits_count(rw);
> -    current->payload_size = (end_position - start_position) / 8;
> -#endif
> -
> -    return 0;
> -}
> -
>  static int FUNC(sei)(CodedBitstreamContext *ctx, RWContext *rw,
>                       H264RawSEI *current)
>  {
> -    int err, k;
> +    int err;
>
>      HEADER("Supplemental Enhancement Information");
>
>      CHECK(FUNC(nal_unit_header)(ctx, rw, &current->nal_unit_header,
>                                  1 << H264_NAL_SEI));
>
> -#ifdef READ
> -    for (k = 0; k < H264_MAX_SEI_PAYLOADS; k++) {
> -        uint32_t payload_type = 0;
> -        uint32_t payload_size = 0;
> -        uint32_t tmp;
> -
> -        while (show_bits(rw, 8) == 0xff) {
> -            fixed(8, ff_byte, 0xff);
> -            payload_type += 255;
> -        }
> -        xu(8, last_payload_type_byte, tmp, 0, 254, 0);
> -        payload_type += tmp;
> -
> -        while (show_bits(rw, 8) == 0xff) {
> -            fixed(8, ff_byte, 0xff);
> -            payload_size += 255;
> -        }
> -        xu(8, last_payload_size_byte, tmp, 0, 254, 0);
> -        payload_size += tmp;
> -
> -        current->payload[k].payload_type = payload_type;
> -        current->payload[k].payload_size = payload_size;
> -
> -        current->payload_count++;
> -        CHECK(FUNC(sei_payload)(ctx, rw, &current->payload[k]));
> -
> -        if (!cbs_h2645_read_more_rbsp_data(rw))
> -            break;
> -    }
> -    if (k >= H264_MAX_SEI_PAYLOADS) {
> -        av_log(ctx->log_ctx, AV_LOG_ERROR, "Too many payloads in "
> -               "SEI message: found %d.\n", k);
> -        return AVERROR_INVALIDDATA;
> -    }
> -#else
> -    for (k = 0; k < current->payload_count; k++) {
> -        PutBitContext start_state;
> -        uint32_t tmp;
> -        int need_size, i;
> -
> -        // Somewhat clumsy: we write the payload twice when
> -        // we don't know the size in advance.  This will mess
> -        // with trace output, but is otherwise harmless.
> -        start_state = *rw;
> -        need_size = !current->payload[k].payload_size;
> -        for (i = 0; i < 1 + need_size; i++) {
> -            *rw = start_state;
> -
> -            tmp = current->payload[k].payload_type;
> -            while (tmp >= 255) {
> -                fixed(8, ff_byte, 0xff);
> -                tmp -= 255;
> -            }
> -            xu(8, last_payload_type_byte, tmp, 0, 254, 0);
> -
> -            tmp = current->payload[k].payload_size;
> -            while (tmp >= 255) {
> -                fixed(8, ff_byte, 0xff);
> -                tmp -= 255;
> -            }
> -            xu(8, last_payload_size_byte, tmp, 0, 254, 0);
> -
> -            CHECK(FUNC(sei_payload)(ctx, rw, &current->payload[k]));
> -        }
> -    }
> -#endif
> +    CHECK(FUNC_SEI(message_list)(ctx, rw, &current->message_list, 1));
>
>      CHECK(FUNC(rbsp_trailing_bits)(ctx, rw));
>
> diff --git a/libavcodec/cbs_h265.h b/libavcodec/cbs_h265.h
> index d8e93e3bb8..738cbeec2c 100644
> --- a/libavcodec/cbs_h265.h
> +++ b/libavcodec/cbs_h265.h
> @@ -658,40 +658,9 @@ typedef struct H265RawSEIAlphaChannelInfo {
>      uint8_t  alpha_channel_clip_type_flag;
>  } H265RawSEIAlphaChannelInfo;
>
> -typedef struct H265RawSEIPayload {
> -    uint32_t payload_type;
> -    uint32_t payload_size;
> -    union {
> -        H265RawSEIBufferingPeriod buffering_period;
> -        H265RawSEIPicTiming pic_timing;
> -        H265RawSEIPanScanRect pan_scan_rect;
> -        SEIRawUserDataRegistered user_data_registered;
> -        SEIRawUserDataUnregistered user_data_unregistered;
> -        H265RawSEIRecoveryPoint recovery_point;
> -        H265RawSEIDisplayOrientation display_orientation;
> -        H265RawSEIActiveParameterSets active_parameter_sets;
> -        H265RawSEIDecodedPictureHash decoded_picture_hash;
> -        H265RawSEITimeCode time_code;
> -        SEIRawMasteringDisplayColourVolume
> -            mastering_display_colour_volume;
> -        SEIRawContentLightLevelInfo content_light_level;
> -        SEIRawAlternativeTransferCharacteristics
> -            alternative_transfer_characteristics;
> -        H265RawSEIAlphaChannelInfo alpha_channel_info;
> -        struct {
> -            uint8_t     *data;
> -            AVBufferRef *data_ref;
> -            size_t       data_length;
> -        } other;
> -    } payload;
> -    H265RawExtensionData extension_data;
> -} H265RawSEIPayload;
> -
>  typedef struct H265RawSEI {
>      H265RawNALUnitHeader nal_unit_header;
> -
> -    H265RawSEIPayload payload[H265_MAX_SEI_PAYLOADS];
> -    uint8_t payload_count;
> +    SEIRawMessageList    message_list;
>  } H265RawSEI;
>
>  typedef struct CodedBitstreamH265Context {
> diff --git a/libavcodec/cbs_h265_syntax_template.c
> b/libavcodec/cbs_h265_syntax_template.c
> index e4cc1a9be8..d09934cfeb 100644
> --- a/libavcodec/cbs_h265_syntax_template.c
> +++ b/libavcodec/cbs_h265_syntax_template.c
> @@ -1596,10 +1596,9 @@ static int
> FUNC(slice_segment_header)(CodedBitstreamContext *ctx, RWContext *rw,
>      return 0;
>  }
>
> -static int FUNC(sei_buffering_period)(CodedBitstreamContext *ctx,
> RWContext *rw,
> -                                      H265RawSEIBufferingPeriod *current,
> -                                      uint32_t *payload_size,
> -                                      int *more_data)
> +static int FUNC(sei_buffering_period)
> +    (CodedBitstreamContext *ctx, RWContext *rw,
> +     H265RawSEIBufferingPeriod *current, SEIMessageState *sei)
>  {
>      CodedBitstreamH265Context *h265 = ctx->priv_data;
>      const H265RawSPS *sps;
> @@ -1687,7 +1686,7 @@ static int
> FUNC(sei_buffering_period)(CodedBitstreamContext *ctx, RWContext *rw,
>
>  #ifdef READ
>      end_pos = get_bits_count(rw);
> -    if (cbs_h265_payload_extension_present(rw, *payload_size,
> +    if (cbs_h265_payload_extension_present(rw, sei->payload_size,
>                                             end_pos - start_pos))
>          flag(use_alt_cpb_params_flag);
>      else
> @@ -1695,20 +1694,21 @@ static int
> FUNC(sei_buffering_period)(CodedBitstreamContext *ctx, RWContext *rw,
>  #else
>      // If unknown extension data exists, then use_alt_cpb_params_flag is
>      // coded in the bitstream and must be written even if it's 0.
> -    if (current->use_alt_cpb_params_flag || *more_data) {
> +    if (current->use_alt_cpb_params_flag || sei->extension_present) {
>          flag(use_alt_cpb_params_flag);
>          // Ensure this bit is not the last in the payload by making the
>          // more_data_in_payload() check evaluate to true, so it may not
>          // be mistaken as something else by decoders.
> -        *more_data = 1;
> +        sei->extension_present = 1;
>      }
>  #endif
>
>      return 0;
>  }
>
> -static int FUNC(sei_pic_timing)(CodedBitstreamContext *ctx, RWContext *rw,
> -                                H265RawSEIPicTiming *current)
> +static int FUNC(sei_pic_timing)
> +    (CodedBitstreamContext *ctx, RWContext *rw,
> +     H265RawSEIPicTiming *current, SEIMessageState *sei)
>  {
>      CodedBitstreamH265Context *h265 = ctx->priv_data;
>      const H265RawSPS *sps;
> @@ -1782,8 +1782,9 @@ static int
> FUNC(sei_pic_timing)(CodedBitstreamContext *ctx, RWContext *rw,
>      return 0;
>  }
>
> -static int FUNC(sei_pan_scan_rect)(CodedBitstreamContext *ctx, RWContext
> *rw,
> -                                   H265RawSEIPanScanRect *current)
> +static int FUNC(sei_pan_scan_rect)
> +    (CodedBitstreamContext *ctx, RWContext *rw,
> +     H265RawSEIPanScanRect *current, SEIMessageState *sei)
>  {
>      int err, i;
>
> @@ -1808,8 +1809,9 @@ static int
> FUNC(sei_pan_scan_rect)(CodedBitstreamContext *ctx, RWContext *rw,
>      return 0;
>  }
>
> -static int FUNC(sei_recovery_point)(CodedBitstreamContext *ctx, RWContext
> *rw,
> -                                    H265RawSEIRecoveryPoint *current)
> +static int FUNC(sei_recovery_point)
> +    (CodedBitstreamContext *ctx, RWContext *rw,
> +     H265RawSEIRecoveryPoint *current, SEIMessageState *sei)
>  {
>      int err;
>
> @@ -1823,8 +1825,9 @@ static int
> FUNC(sei_recovery_point)(CodedBitstreamContext *ctx, RWContext *rw,
>      return 0;
>  }
>
> -static int FUNC(sei_display_orientation)(CodedBitstreamContext *ctx,
> RWContext *rw,
> -                                         H265RawSEIDisplayOrientation
> *current)
> +static int FUNC(sei_display_orientation)
> +    (CodedBitstreamContext *ctx, RWContext *rw,
> +     H265RawSEIDisplayOrientation *current, SEIMessageState *sei)
>  {
>      int err;
>
> @@ -1841,8 +1844,9 @@ static int
> FUNC(sei_display_orientation)(CodedBitstreamContext *ctx, RWContext *
>      return 0;
>  }
>
> -static int FUNC(sei_active_parameter_sets)(CodedBitstreamContext *ctx,
> RWContext *rw,
> -                                           H265RawSEIActiveParameterSets
> *current)
> +static int FUNC(sei_active_parameter_sets)
> +    (CodedBitstreamContext *ctx, RWContext *rw,
> +     H265RawSEIActiveParameterSets *current, SEIMessageState *sei)
>  {
>      CodedBitstreamH265Context *h265 = ctx->priv_data;
>      const H265RawVPS *vps;
> @@ -1877,8 +1881,9 @@ static int
> FUNC(sei_active_parameter_sets)(CodedBitstreamContext *ctx, RWContext
>      return 0;
>  }
>
> -static int FUNC(sei_decoded_picture_hash)(CodedBitstreamContext *ctx,
> RWContext *rw,
> -                                          H265RawSEIDecodedPictureHash
> *current)
> +static int FUNC(sei_decoded_picture_hash)
> +    (CodedBitstreamContext *ctx, RWContext *rw,
> +     H265RawSEIDecodedPictureHash *current, SEIMessageState *sei)
>  {
>      CodedBitstreamH265Context *h265 = ctx->priv_data;
>      const H265RawSPS *sps = h265->active_sps;
> @@ -1908,8 +1913,9 @@ static int
> FUNC(sei_decoded_picture_hash)(CodedBitstreamContext *ctx, RWContext
>      return 0;
>  }
>
> -static int FUNC(sei_time_code)(CodedBitstreamContext *ctx, RWContext *rw,
> -                               H265RawSEITimeCode *current)
> +static int FUNC(sei_time_code)
> +    (CodedBitstreamContext *ctx, RWContext *rw,
> +     H265RawSEITimeCode *current, SEIMessageState *sei)
>  {
>      int err, i;
>
> @@ -1958,9 +1964,9 @@ static int FUNC(sei_time_code)(CodedBitstreamContext
> *ctx, RWContext *rw,
>      return 0;
>  }
>
> -static int FUNC(sei_alpha_channel_info)(CodedBitstreamContext *ctx,
> -                                        RWContext *rw,
> -                                        H265RawSEIAlphaChannelInfo
> *current)
> +static int FUNC(sei_alpha_channel_info)
> +    (CodedBitstreamContext *ctx, RWContext *rw,
> +     H265RawSEIAlphaChannelInfo *current, SEIMessageState *sei)
>  {
>      int err, length;
>
> @@ -1986,156 +1992,10 @@ static int
> FUNC(sei_alpha_channel_info)(CodedBitstreamContext *ctx,
>      return 0;
>  }
>
> -static int FUNC(payload_extension)(CodedBitstreamContext *ctx, RWContext
> *rw,
> -                                   H265RawExtensionData *current,
> uint32_t payload_size,
> -                                   int cur_pos)
> -{
> -    int err;
> -    size_t byte_length, k;
> -
> -#ifdef READ
> -    GetBitContext tmp;
> -    int bits_left, payload_zero_bits;
> -
> -    if (!cbs_h265_payload_extension_present(rw, payload_size, cur_pos))
> -        return 0;
> -
> -    bits_left = 8 * payload_size - cur_pos;
> -    tmp = *rw;
> -    if (bits_left > 8)
> -        skip_bits_long(&tmp, bits_left - 8);
> -    payload_zero_bits = get_bits(&tmp, FFMIN(bits_left, 8));
> -    if (!payload_zero_bits)
> -        return AVERROR_INVALIDDATA;
> -    payload_zero_bits = ff_ctz(payload_zero_bits);
> -    current->bit_length = bits_left - payload_zero_bits - 1;
> -    allocate(current->data, (current->bit_length + 7) / 8);
> -#endif
> -
> -    byte_length = (current->bit_length + 7) / 8;
> -    for (k = 0; k < byte_length; k++) {
> -        int length = FFMIN(current->bit_length - k * 8, 8);
> -        xu(length, reserved_payload_extension_data, current->data[k],
> -           0, MAX_UINT_BITS(length), 0);
> -    }
> -
> -    return 0;
> -}
> -
> -static int FUNC(sei_payload)(CodedBitstreamContext *ctx, RWContext *rw,
> -                             H265RawSEIPayload *current, int prefix)
> -{
> -    int err, i;
> -    int start_position, current_position;
> -    int more_data = !!current->extension_data.bit_length;
> -
> -#ifdef READ
> -    start_position = get_bits_count(rw);
> -#else
> -    start_position = put_bits_count(rw);
> -#endif
> -
> -    switch (current->payload_type) {
> -#define SEI_TYPE_CHECK_VALID(name, prefix_valid, suffix_valid) do { \
> -            if (prefix && !prefix_valid) { \
> -                av_log(ctx->log_ctx, AV_LOG_ERROR, "SEI type %s invalid "
> \
> -                       "as prefix SEI!\n", #name); \
> -                return AVERROR_INVALIDDATA; \
> -            } \
> -            if (!prefix && !suffix_valid) { \
> -                av_log(ctx->log_ctx, AV_LOG_ERROR, "SEI type %s invalid "
> \
> -                       "as suffix SEI!\n", #name); \
> -                return AVERROR_INVALIDDATA; \
> -            } \
> -        } while (0)
> -#define SEI_TYPE_N(type, prefix_valid, suffix_valid, name) \
> -    case HEVC_SEI_TYPE_ ## type: \
> -        SEI_TYPE_CHECK_VALID(name, prefix_valid, suffix_valid); \
> -        CHECK(FUNC(sei_ ## name)(ctx, rw, &current->payload.name)); \
> -        break
> -#define SEI_TYPE_S(type, prefix_valid, suffix_valid, name) \
> -    case HEVC_SEI_TYPE_ ## type: \
> -        SEI_TYPE_CHECK_VALID(name, prefix_valid, suffix_valid); \
> -        CHECK(FUNC(sei_ ## name)(ctx, rw, &current->payload.name, \
> -                                 &current->payload_size)); \
> -        break
> -#define SEI_TYPE_E(type, prefix_valid, suffix_valid, name) \
> -    case HEVC_SEI_TYPE_ ## type: \
> -        SEI_TYPE_CHECK_VALID(name, prefix_valid, suffix_valid); \
> -        CHECK(FUNC(sei_ ## name)(ctx, rw, &current->payload.name, \
> -                                 &current->payload_size, \
> -                                 &more_data)); \
> -        break
> -
> -#define SEI_TYPE_N2(type, prefix_valid, suffix_valid, name) \
> -    case HEVC_SEI_TYPE_ ## type: \
> -        SEI_TYPE_CHECK_VALID(name, prefix_valid, suffix_valid); \
> -        CHECK(FUNC_SEI(sei_ ## name)(ctx, rw, &current->payload.name)); \
> -        break
> -#define SEI_TYPE_S2(type, prefix_valid, suffix_valid, name) \
> -    case HEVC_SEI_TYPE_ ## type: \
> -        SEI_TYPE_CHECK_VALID(name, prefix_valid, suffix_valid); \
> -        CHECK(FUNC_SEI(sei_ ## name)(ctx, rw, &current->payload.name, \
> -                                     &current->payload_size)); \
> -        break
> -
> -        SEI_TYPE_E(BUFFERING_PERIOD,         1, 0, buffering_period);
> -        SEI_TYPE_N(PICTURE_TIMING,           1, 0, pic_timing);
> -        SEI_TYPE_N(PAN_SCAN_RECT,            1, 0, pan_scan_rect);
> -        SEI_TYPE_S2(USER_DATA_REGISTERED_ITU_T_T35,
> -                                             1, 1, user_data_registered);
> -        SEI_TYPE_S2(USER_DATA_UNREGISTERED,  1, 1,
> user_data_unregistered);
> -        SEI_TYPE_N(RECOVERY_POINT,           1, 0, recovery_point);
> -        SEI_TYPE_N(DISPLAY_ORIENTATION,      1, 0, display_orientation);
> -        SEI_TYPE_N(ACTIVE_PARAMETER_SETS,    1, 0, active_parameter_sets);
> -        SEI_TYPE_N(DECODED_PICTURE_HASH,     0, 1, decoded_picture_hash);
> -        SEI_TYPE_N(TIME_CODE,                1, 0, time_code);
> -        SEI_TYPE_N2(MASTERING_DISPLAY_INFO,  1, 0,
> mastering_display_colour_volume);
> -        SEI_TYPE_N2(CONTENT_LIGHT_LEVEL_INFO,1, 0, content_light_level);
> -        SEI_TYPE_N2(ALTERNATIVE_TRANSFER_CHARACTERISTICS,
> -                                             1, 0,
> alternative_transfer_characteristics);
> -        SEI_TYPE_N(ALPHA_CHANNEL_INFO,       1, 0, alpha_channel_info);
> -
> -#undef SEI_TYPE
> -    default:
> -        {
> -#ifdef READ
> -            current->payload.other.data_length = current->payload_size;
> -#endif
> -            allocate(current->payload.other.data,
> current->payload.other.data_length);
> -
> -            for (i = 0; i < current->payload_size; i++)
> -                xu(8, payload_byte[i], current->payload.other.data[i], 0,
> 255,
> -                   1, i);
> -        }
> -    }
> -
> -    // more_data_in_payload()
> -#ifdef READ
> -    current_position = get_bits_count(rw) - start_position;
> -    if (current_position < 8 * current->payload_size) {
> -#else
> -    current_position = put_bits_count(rw) - start_position;
> -    if (byte_alignment(rw) || more_data) {
> -#endif
> -        CHECK(FUNC(payload_extension)(ctx, rw, &current->extension_data,
> -                                      current->payload_size,
> current_position));
> -        fixed(1, bit_equal_to_one, 1);
> -        while (byte_alignment(rw))
> -            fixed(1, bit_equal_to_zero, 0);
> -    }
> -
> -#ifdef WRITE
> -    current->payload_size = (put_bits_count(rw) - start_position) >> 3;
> -#endif
> -
> -    return 0;
> -}
> -
>  static int FUNC(sei)(CodedBitstreamContext *ctx, RWContext *rw,
>                       H265RawSEI *current, int prefix)
>  {
> -    int err, k;
> +    int err;
>
>      if (prefix)
>          HEADER("Prefix Supplemental Enhancement Information");
> @@ -2146,72 +2006,7 @@ static int FUNC(sei)(CodedBitstreamContext *ctx,
> RWContext *rw,
>                                  prefix ? HEVC_NAL_SEI_PREFIX
>                                         : HEVC_NAL_SEI_SUFFIX));
>
> -#ifdef READ
> -    for (k = 0; k < H265_MAX_SEI_PAYLOADS; k++) {
> -        uint32_t payload_type = 0;
> -        uint32_t payload_size = 0;
> -        uint32_t tmp;
> -
> -        while (show_bits(rw, 8) == 0xff) {
> -            fixed(8, ff_byte, 0xff);
> -            payload_type += 255;
> -        }
> -        xu(8, last_payload_type_byte, tmp, 0, 254, 0);
> -        payload_type += tmp;
> -
> -        while (show_bits(rw, 8) == 0xff) {
> -            fixed(8, ff_byte, 0xff);
> -            payload_size += 255;
> -        }
> -        xu(8, last_payload_size_byte, tmp, 0, 254, 0);
> -        payload_size += tmp;
> -
> -        current->payload[k].payload_type = payload_type;
> -        current->payload[k].payload_size = payload_size;
> -
> -        current->payload_count++;
> -        CHECK(FUNC(sei_payload)(ctx, rw, &current->payload[k], prefix));
> -
> -        if (!cbs_h2645_read_more_rbsp_data(rw))
> -            break;
> -    }
> -    if (k >= H265_MAX_SEI_PAYLOADS) {
> -        av_log(ctx->log_ctx, AV_LOG_ERROR, "Too many payloads in "
> -               "SEI message: found %d.\n", k);
> -        return AVERROR_INVALIDDATA;
> -    }
> -#else
> -    for (k = 0; k < current->payload_count; k++) {
> -        PutBitContext start_state;
> -        uint32_t tmp;
> -        int need_size, i;
> -
> -        // Somewhat clumsy: we write the payload twice when
> -        // we don't know the size in advance.  This will mess
> -        // with trace output, but is otherwise harmless.
> -        start_state = *rw;
> -        need_size = !current->payload[k].payload_size;
> -        for (i = 0; i < 1 + need_size; i++) {
> -            *rw = start_state;
> -
> -            tmp = current->payload[k].payload_type;
> -            while (tmp >= 255) {
> -                fixed(8, ff_byte, 0xff);
> -                tmp -= 255;
> -            }
> -            xu(8, last_payload_type_byte, tmp, 0, 254, 0);
> -
> -            tmp = current->payload[k].payload_size;
> -            while (tmp >= 255) {
> -                fixed(8, ff_byte, 0xff);
> -                tmp -= 255;
> -            }
> -            xu(8, last_payload_size_byte, tmp, 0, 254, 0);
> -
> -            CHECK(FUNC(sei_payload)(ctx, rw, &current->payload[k],
> prefix));
> -        }
> -    }
> -#endif
> +    CHECK(FUNC_SEI(message_list)(ctx, rw, &current->message_list,
> prefix));
>
>      CHECK(FUNC(rbsp_trailing_bits)(ctx, rw));
>
> diff --git a/libavcodec/cbs_sei.c b/libavcodec/cbs_sei.c
> new file mode 100644
> index 0000000000..323997b600
> --- /dev/null
> +++ b/libavcodec/cbs_sei.c
> @@ -0,0 +1,369 @@
> +/*
> + * 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 "cbs.h"
> +#include "cbs_internal.h"
> +#include "cbs_h264.h"
> +#include "cbs_h265.h"
> +#include "cbs_sei.h"
> +
> +static void cbs_free_user_data_registered(void *opaque, uint8_t *data)
> +{
> +    SEIRawUserDataRegistered *udr = (SEIRawUserDataRegistered*)data;
> +    av_buffer_unref(&udr->data_ref);
> +    av_free(udr);
> +}
> +
> +static void cbs_free_user_data_unregistered(void *opaque, uint8_t *data)
> +{
> +    SEIRawUserDataUnregistered *udu = (SEIRawUserDataUnregistered*)data;
> +    av_buffer_unref(&udu->data_ref);
> +    av_free(udu);
> +}
> +
> +int ff_cbs_sei_alloc_message_payload(SEIRawMessage *message,
> +                                     const SEIMessageTypeDescriptor *desc)
> +{
> +    void (*free_func)(void*, uint8_t*);
> +
> +    av_assert0(message->payload     == NULL &&
> +               message->payload_ref == NULL);
> +    message->payload_type = desc->type;
> +
> +    if (desc->type == SEI_TYPE_USER_DATA_REGISTERED_ITU_T_T35)
> +        free_func = &cbs_free_user_data_registered;
> +    else if (desc->type == SEI_TYPE_USER_DATA_UNREGISTERED)
> +        free_func = &cbs_free_user_data_unregistered;
> +    else
> +        free_func = NULL;
> +
> +    if (free_func) {
> +        message->payload = av_mallocz(desc->size);
> +        if (!message->payload)
> +            return AVERROR(ENOMEM);
> +        message->payload_ref =
> +            av_buffer_create(message->payload, desc->size,
> +                             free_func, NULL, 0);
>
leak  message->payload if av_buffer_create returns error

> +    } else {
> +        message->payload_ref = av_buffer_alloc(desc->size);
> +    }
> +    if (!message->payload_ref)
> +        return AVERROR(ENOMEM);
> +    message->payload = message->payload_ref->data;
> +
> +    return 0;
> +}
> +
> +int ff_cbs_sei_list_add(SEIRawMessageList *list)
> +{
> +    void *ptr;
> +    int old_count = list->nb_messages_allocated;
> +
> +    av_assert0(list->nb_messages <= old_count);
> +    if (list->nb_messages + 1 > old_count) {
> +        int new_count = 2 * old_count + 1;
> +
> +        ptr = av_realloc_array(list->messages,
> +                               new_count, sizeof(*list->messages));
> +        if (!ptr)
> +            return AVERROR(ENOMEM);
> +
> +        list->messages = ptr;
> +        list->nb_messages_allocated = new_count;
> +
> +        // Zero the newly-added entries.
> +        memset(list->messages + old_count, 0,
> +               (new_count - old_count) * sizeof(*list->messages));
> +    }
> +    ++list->nb_messages;
> +    return 0;
> +}
> +
> +void ff_cbs_sei_free_message_list(SEIRawMessageList *list)
> +{
> +    for (int i = 0; i < list->nb_messages; i++) {
> +        SEIRawMessage *message = &list->messages[i];
> +        av_buffer_unref(&message->payload_ref);
> +        av_buffer_unref(&message->extension_data_ref);
> +    }
> +    av_free(list->messages);
> +}
> +
> +static int cbs_sei_get_unit(CodedBitstreamContext *ctx,
> +                            CodedBitstreamFragment *au,
> +                            int prefix,
> +                            CodedBitstreamUnit **sei_unit)
> +{
> +    CodedBitstreamUnit *unit;
> +    int sei_type, highest_vcl_type, err, i, position;
> +
> +    switch (ctx->codec->codec_id) {
> +    case AV_CODEC_ID_H264:
> +        // (We can ignore auxiliary slices because we only have prefix
> +        // SEI in H.264 and an auxiliary picture must always follow a
> +        // primary picture.)
> +        highest_vcl_type = H264_NAL_IDR_SLICE;
> +        if (prefix)
> +            sei_type = H264_NAL_SEI;
> +        else
> +            return AVERROR(EINVAL);
> +        break;
> +    case AV_CODEC_ID_H265:
> +        highest_vcl_type = HEVC_NAL_RSV_VCL31;
> +        if (prefix)
> +            sei_type = HEVC_NAL_SEI_PREFIX;
> +        else
> +            sei_type = HEVC_NAL_SEI_SUFFIX;
> +        break;
> +    default:
> +        return AVERROR(EINVAL);
> +    }
> +
> +    // Find an existing SEI NAL unit of the right type.
> +    unit = NULL;
> +    for (i = 0; i < au->nb_units; i++) {
> +        if (au->units[i].type == sei_type) {
> +            unit = &au->units[i];
> +            break;
> +        }
> +    }
> +
> +    if (unit) {
> +        *sei_unit = unit;
> +        return 0;
> +    }
> +
> +    // Need to add a new SEI NAL unit ...
> +    if (prefix) {
> +        // ... before the first VCL NAL unit.
> +        for (i = 0; i < au->nb_units; i++) {
> +            if (au->units[i].type < highest_vcl_type)
> +                break;
> +        }
> +        position = i;
> +    } else {
> +        // ... after the last VCL NAL unit.
> +        for (i = au->nb_units; i >= 0; i--) {
>
This will access au->units[au->nb_units], will it read beyond the boundary?


> +            if (au->units[i].type < highest_vcl_type)
> +                break;
> +        }
> +        if (i < 0) {
> +            // No VCL units; just put it at the end.
> +            position = -1;
> +        } else {
> +            position = i + 1;
> +        }
> +    }
> +
> +    err = ff_cbs_insert_unit_content(au, position, sei_type,
> +                                     NULL, NULL);
> +    if (err < 0)
> +        return err;
> +    unit = &au->units[position];
> +    unit->type = sei_type;
> +
> +    err = ff_cbs_alloc_unit_content2(ctx, unit);
> +    if (err < 0)
> +        return err;
> +
> +    switch (ctx->codec->codec_id) {
> +    case AV_CODEC_ID_H264:
> +        {
> +            H264RawSEI sei = {
> +                .nal_unit_header = {
> +                    .nal_ref_idc   = 0,
> +                    .nal_unit_type = sei_type,
> +                },
> +            };
> +            memcpy(unit->content, &sei, sizeof(sei));
> +        }
> +        break;
> +    case AV_CODEC_ID_H265:
> +        {
> +            H265RawSEI 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);
> +    }
> +
> +    *sei_unit = unit;
> +    return 0;
> +}
> +
> +static int cbs_sei_get_message_list(CodedBitstreamContext *ctx,
> +                                    CodedBitstreamUnit *unit,
> +                                    SEIRawMessageList **list)
> +{
> +    switch (ctx->codec->codec_id) {
> +    case AV_CODEC_ID_H264:
> +        {
> +            H264RawSEI *sei = unit->content;
> +            if (unit->type != H264_NAL_SEI)
> +                return AVERROR(EINVAL);
> +            *list = &sei->message_list;
> +        }
> +        break;
> +    case AV_CODEC_ID_H265:
> +        {
> +            H265RawSEI *sei = unit->content;
> +            if (unit->type != HEVC_NAL_SEI_PREFIX &&
> +                unit->type != HEVC_NAL_SEI_SUFFIX)
> +                return AVERROR(EINVAL);
> +            *list = &sei->message_list;
> +        }
> +        break;
> +    default:
> +        return AVERROR(EINVAL);
> +    }
> +
> +    return 0;
> +}
> +
> +int ff_cbs_sei_add_message(CodedBitstreamContext *ctx,
> +                           CodedBitstreamFragment *au,
> +                           int prefix,
> +                           uint32_t     payload_type,
> +                           void        *payload_data,
> +                           AVBufferRef *payload_buf)
> +{
> +    const SEIMessageTypeDescriptor *desc;
> +    CodedBitstreamUnit *unit;
> +    SEIRawMessageList *list;
> +    SEIRawMessage *message;
> +    AVBufferRef *payload_ref;
> +    int err;
> +
> +    desc = ff_cbs_sei_find_type(ctx, payload_type);
> +    if (!desc)
> +        return AVERROR(EINVAL);
> +
> +    if (payload_buf) {
> +        payload_ref = av_buffer_ref(payload_buf);
> +        if (!payload_ref)
> +            return AVERROR(ENOMEM);
> +    } else {
> +        payload_ref = NULL;
> +    }
> +
> +    // Find an existing SEI unit or make a new one to add to.
> +    err = cbs_sei_get_unit(ctx, au, prefix, &unit);
> +    if (err < 0)
> +        return err;
> +
> +    // Find the message list inside the codec-dependent unit.
> +    err = cbs_sei_get_message_list(ctx, unit, &list);
> +    if (err < 0)
> +        return err;
> +
> +    // Add a new message to the message list.
> +    err = ff_cbs_sei_list_add(list);
> +    if (err < 0)
> +        return err;
> +
> +    message = &list->messages[list->nb_messages - 1];
> +
> +    message->payload_type = payload_type;
> +    message->payload      = payload_data;
> +    message->payload_ref  = payload_ref;
> +
> +    return 0;
> +}
> +
> +int ff_cbs_sei_find_message(CodedBitstreamContext *ctx,
> +                            CodedBitstreamFragment *au,
> +                            uint32_t payload_type,
> +                            SEIRawMessage **iter)
> +{
> +    int err, i, j, found;
> +
> +    found = 0;
> +    for (i = 0; i < au->nb_units; i++) {
> +        CodedBitstreamUnit *unit = &au->units[i];
> +        SEIRawMessageList *list;
> +
> +        err = cbs_sei_get_message_list(ctx, unit, &list);
> +        if (err < 0)
> +            continue;
> +
> +        for (j = 0; j < list->nb_messages; j++) {
> +            SEIRawMessage *message = &list->messages[j];
> +
> +            if (message->payload_type == payload_type) {
> +                if (!*iter || found) {
> +                    *iter = message;
> +                    return 0;
> +                }
> +                if (message == *iter)
> +                    found = 1;
> +            }
> +        }
> +    }
> +
> +    return AVERROR(ENOENT);
> +}
> +
> +static void cbs_sei_delete_message(SEIRawMessageList *list,
> +                                   int position)
> +{
> +    SEIRawMessage *message;
> +
> +    av_assert0(0 <= position && position < list->nb_messages);
> +
> +    message = &list->messages[position];
> +    av_buffer_unref(&message->payload_ref);
> +    av_buffer_unref(&message->extension_data_ref);
> +
> +    --list->nb_messages;
> +
> +    if (list->nb_messages > 0) {
> +        memmove(list->messages + position,
> +                list->messages + position + 1,
> +                (list->nb_messages - position) * sizeof(*list->messages));
> +    }
> +}
> +
> +void ff_cbs_sei_delete_message_type(CodedBitstreamContext *ctx,
> +                                    CodedBitstreamFragment *au,
> +                                    uint32_t payload_type)
> +{
> +    int err, i, j;
> +
> +    for (i = 0; i < au->nb_units; i++) {
> +        CodedBitstreamUnit *unit = &au->units[i];
> +        SEIRawMessageList *list;
> +
> +        err = cbs_sei_get_message_list(ctx, unit, &list);
> +        if (err < 0)
> +            continue;
> +
> +        for (j = 0; j < list->nb_messages;) {
> +            if (list->messages[j].payload_type == payload_type)
> +                cbs_sei_delete_message(list, j);
> +            else
> +                ++j;
> +        }
>
loop from end to start may be better from performance and code size aspect.

+    }
> +}
> diff --git a/libavcodec/cbs_sei.h b/libavcodec/cbs_sei.h
> index 95beabf4d7..5ce4ad3ccd 100644
> --- a/libavcodec/cbs_sei.h
> +++ b/libavcodec/cbs_sei.h
> @@ -21,8 +21,132 @@
>
>  #include <stddef.h>
>  #include <stdint.h>
> +
>  #include "libavutil/buffer.h"
>
> +#include "cbs.h"
> +
> +// SEI payload types form a common namespace between the H.264, H.265
> +// and H.266 standards.  A given payload type always has the same
> +// meaning, but some names have different payload types in different
> +// standards (e.g. scalable-nesting is 30 in H.264 but 133 in H.265).
> +// The content of the payload data depends on the standard, though
> +// many generic parts have the same interpretation everywhere (such as
> +// mastering-display-colour-volume and user-data-unregistered).
> +enum {
> +    SEI_TYPE_BUFFERING_PERIOD                            = 0,
> +    SEI_TYPE_PIC_TIMING                                  = 1,
> +    SEI_TYPE_PAN_SCAN_RECT                               = 2,
> +    SEI_TYPE_FILLER_PAYLOAD                              = 3,
> +    SEI_TYPE_USER_DATA_REGISTERED_ITU_T_T35              = 4,
> +    SEI_TYPE_USER_DATA_UNREGISTERED                      = 5,
> +    SEI_TYPE_RECOVERY_POINT                              = 6,
> +    SEI_TYPE_DEC_REF_PIC_MARKING_REPETITION              = 7,
> +    SEI_TYPE_SPARE_PIC                                   = 8,
> +    SEI_TYPE_SCENE_INFO                                  = 9,
> +    SEI_TYPE_SUB_SEQ_INFO                                = 10,
> +    SEI_TYPE_SUB_SEQ_LAYER_CHARACTERISTICS               = 11,
> +    SEI_TYPE_SUB_SEQ_CHARACTERISTICS                     = 12,
> +    SEI_TYPE_FULL_FRAME_FREEZE                           = 13,
> +    SEI_TYPE_FULL_FRAME_FREEZE_RELEASE                   = 14,
> +    SEI_TYPE_FULL_FRAME_SNAPSHOT                         = 15,
> +    SEI_TYPE_PROGRESSIVE_REFINEMENT_SEGMENT_START        = 16,
> +    SEI_TYPE_PROGRESSIVE_REFINEMENT_SEGMENT_END          = 17,
> +    SEI_TYPE_MOTION_CONSTRAINED_SLICE_GROUP_SET          = 18,
> +    SEI_TYPE_FILM_GRAIN_CHARACTERISTICS                  = 19,
> +    SEI_TYPE_DEBLOCKING_FILTER_DISPLAY_PREFERENCE        = 20,
> +    SEI_TYPE_STEREO_VIDEO_INFO                           = 21,
> +    SEI_TYPE_POST_FILTER_HINT                            = 22,
> +    SEI_TYPE_TONE_MAPPING_INFO                           = 23,
> +    SEI_TYPE_SCALABILITY_INFO                            = 24,
> +    SEI_TYPE_SUB_PIC_SCALABLE_LAYER                      = 25,
> +    SEI_TYPE_NON_REQUIRED_LAYER_REP                      = 26,
> +    SEI_TYPE_PRIORITY_LAYER_INFO                         = 27,
> +    SEI_TYPE_LAYERS_NOT_PRESENT_4                        = 28,
> +    SEI_TYPE_LAYER_DEPENDENCY_CHANGE                     = 29,
> +    SEI_TYPE_SCALABLE_NESTING_4                          = 30,
> +    SEI_TYPE_BASE_LAYER_TEMPORAL_HRD                     = 31,
> +    SEI_TYPE_QUALITY_LAYER_INTEGRITY_CHECK               = 32,
> +    SEI_TYPE_REDUNDANT_PIC_PROPERTY                      = 33,
> +    SEI_TYPE_TL0_DEP_REP_INDEX                           = 34,
> +    SEI_TYPE_TL_SWITCHING_POINT                          = 35,
> +    SEI_TYPE_PARALLEL_DECODING_INFO                      = 36,
> +    SEI_TYPE_MVC_SCALABLE_NESTING                        = 37,
> +    SEI_TYPE_VIEW_SCALABILITY_INFO                       = 38,
> +    SEI_TYPE_MULTIVIEW_SCENE_INFO_4                      = 39,
> +    SEI_TYPE_MULTIVIEW_ACQUISITION_INFO_4                = 40,
> +    SEI_TYPE_NON_REQUIRED_VIEW_COMPONENT                 = 41,
> +    SEI_TYPE_VIEW_DEPENDENCY_CHANGE                      = 42,
> +    SEI_TYPE_OPERATION_POINTS_NOT_PRESENT                = 43,
> +    SEI_TYPE_BASE_VIEW_TEMPORAL_HRD                      = 44,
> +    SEI_TYPE_FRAME_PACKING_ARRANGEMENT                   = 45,
> +    SEI_TYPE_MULTIVIEW_VIEW_POSITION_4                   = 46,
> +    SEI_TYPE_DISPLAY_ORIENTATION                         = 47,
> +    SEI_TYPE_MVCD_SCALABLE_NESTING                       = 48,
> +    SEI_TYPE_MVCD_VIEW_SCALABILITY_INFO                  = 49,
> +    SEI_TYPE_DEPTH_REPRESENTATION_INFO_4                 = 50,
> +    SEI_TYPE_THREE_DIMENSIONAL_REFERENCE_DISPLAYS_INFO_4 = 51,
> +    SEI_TYPE_DEPTH_TIMING                                = 52,
> +    SEI_TYPE_DEPTH_SAMPLING_INFO                         = 53,
> +    SEI_TYPE_CONSTRAINED_DEPTH_PARAMETER_SET_IDENTIFIER  = 54,
> +    SEI_TYPE_GREEN_METADATA                              = 56,
> +    SEI_TYPE_STRUCTURE_OF_PICTURES_INFO                  = 128,
> +    SEI_TYPE_ACTIVE_PARAMETER_SETS                       = 129,

+    SEI_TYPE_DECODING_UNIT_INFO                          = 130,
> +    SEI_TYPE_TEMPORAL_SUB_LAYER_ZERO_IDX                 = 131,
> +    SEI_TYPE_DECODED_PICTURE_HASH                        = 132,
> +    SEI_TYPE_SCALABLE_NESTING_5                          = 133,
> +    SEI_TYPE_REGION_REFRESH_INFO                         = 134,
> +    SEI_TYPE_NO_DISPLAY                                  = 135,
> +    SEI_TYPE_TIME_CODE                                   = 136,
> +    SEI_TYPE_MASTERING_DISPLAY_COLOUR_VOLUME             = 137,
> +    SEI_TYPE_SEGMENTED_RECT_FRAME_PACKING_ARRANGEMENT    = 138,
> +    SEI_TYPE_TEMPORAL_MOTION_CONSTRAINED_TILE_SETS       = 139,
> +    SEI_TYPE_CHROMA_RESAMPLING_FILTER_HINT               = 140,
> +    SEI_TYPE_KNEE_FUNCTION_INFO                          = 141,
> +    SEI_TYPE_COLOUR_REMAPPING_INFO                       = 142,
> +    SEI_TYPE_DEINTERLACED_FIELD_IDENTIFICATION           = 143,
> +    SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO                    = 144,
> +    SEI_TYPE_DEPENDENT_RAP_INDICATION                    = 145,
> +    SEI_TYPE_CODED_REGION_COMPLETION                     = 146,
> +    SEI_TYPE_ALTERNATIVE_TRANSFER_CHARACTERISTICS        = 147,
> +    SEI_TYPE_AMBIENT_VIEWING_ENVIRONMENT                 = 148,
> +    SEI_TYPE_CONTENT_COLOUR_VOLUME                       = 149,
> +    SEI_TYPE_EQUIRECTANGULAR_PROJECTION                  = 150,
> +    SEI_TYPE_CUBEMAP_PROJECTION                          = 151,
> +    SEI_TYPE_FISHEYE_VIDEO_INFO                          = 152,
> +    SEI_TYPE_SPHERE_ROTATION                             = 154,
> +    SEI_TYPE_REGIONWISE_PACKING                          = 155,
> +    SEI_TYPE_OMNI_VIEWPORT                               = 156,
> +    SEI_TYPE_REGIONAL_NESTING                            = 157,
> +    SEI_TYPE_MCTS_EXTRACTION_INFO_SETS                   = 158,
> +    SEI_TYPE_MCTS_EXTRACTION_INFO_NESTING                = 159,
> +    SEI_TYPE_LAYERS_NOT_PRESENT_5                        = 160,
> +    SEI_TYPE_INTER_LAYER_CONSTRAINED_TILE_SETS           = 161,
> +    SEI_TYPE_BSP_NESTING                                 = 162,
> +    SEI_TYPE_BSP_INITIAL_ARRIVAL_TIME                    = 163,
> +    SEI_TYPE_SUB_BITSTREAM_PROPERTY                      = 164,
> +    SEI_TYPE_ALPHA_CHANNEL_INFO                          = 165,
> +    SEI_TYPE_OVERLAY_INFO                                = 166,
> +    SEI_TYPE_TEMPORAL_MV_PREDICTION_CONSTRAINTS          = 167,
> +    SEI_TYPE_FRAME_FIELD_INFO                            = 168,
> +    SEI_TYPE_THREE_DIMENSIONAL_REFERENCE_DISPLAYS_INFO   = 176,
> +    SEI_TYPE_DEPTH_REPRESENTATION_INFO_5                 = 177,
> +    SEI_TYPE_MULTIVIEW_SCENE_INFO_5                      = 178,
> +    SEI_TYPE_MULTIVIEW_ACQUISITION_INFO_5                = 179,
> +    SEI_TYPE_MULTIVIEW_VIEW_POSITION_5                   = 180,
> +    SEI_TYPE_ALTERNATIVE_DEPTH_INFO                      = 181,
> +    SEI_TYPE_SEI_MANIFEST                                = 200,
> +    SEI_TYPE_SEI_PREFIX_INDICATION                       = 201,
> +    SEI_TYPE_ANNOTATED_REGIONS                           = 202,
> +    SEI_TYPE_SUBPIC_LEVEL_INFO                           = 203,
> +    SEI_TYPE_SAMPLE_ASPECT_RATIO_INFO                    = 204,
> +};
> +
> +
> +typedef struct SEIRawFillerPayload {
> +    uint32_t payload_size;
> +} SEIRawFillerPayload;
>
>  typedef struct SEIRawUserDataRegistered {
>      uint8_t      itu_t_t35_country_code;
> @@ -57,4 +181,135 @@ typedef struct
> SEIRawAlternativeTransferCharacteristics {
>      uint8_t preferred_transfer_characteristics;
>  } SEIRawAlternativeTransferCharacteristics;
>
> +typedef struct SEIRawMessage {
> +    uint32_t     payload_type;
> +    uint32_t     payload_size;
> +    void        *payload;
> +    AVBufferRef *payload_ref;
> +    uint8_t     *extension_data;
> +    AVBufferRef *extension_data_ref;
> +    size_t       extension_bit_length;
> +} SEIRawMessage;
> +
> +typedef struct SEIRawMessageList {
> +    SEIRawMessage *messages;
> +    int         nb_messages;
> +    int         nb_messages_allocated;
> +} SEIRawMessageList;
> +
> +
> +typedef struct SEIMessageState {
> +    // 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;
> +} SEIMessageState;
> +
> +struct GetBitContext;
> +struct PutBitContext;
> +
> +typedef int (*SEIMessageReadFunction)(CodedBitstreamContext *ctx,
> +                                      struct GetBitContext *rw,
> +                                      void *current,
> +                                      SEIMessageState *sei);
> +
> +typedef int (*SEIMessageWriteFunction)(CodedBitstreamContext *ctx,
> +                                       struct PutBitContext *rw,
> +                                       void *current,
> +                                       SEIMessageState *sei);
> +
> +typedef struct SEIMessageTypeDescriptor {
> +    // Payload type for the message.  (-1 in this field ends a list.)
> +    int     type;
> +    // Valid in a prefix SEI NAL unit (always for H.264).
> +    uint8_t prefix;
> +    // Valid in a suffix SEI NAL unit (never for H.264).
> +    uint8_t suffix;
> +    // Size of the decomposed structure.
> +    size_t  size;
> +    // Read bitstream into SEI message.
> +    SEIMessageReadFunction  read;
> +    // Write bitstream from SEI message.
> +    SEIMessageWriteFunction write;
> +} SEIMessageTypeDescriptor;
> +
> +// Macro for the read/write pair.  The clumsy cast is needed because the
> +// current pointer is typed in all of the read/write functions but has to
> +// be void here to fit all cases.
> +#define SEI_MESSAGE_RW(codec, name) \
> +    .read  = (SEIMessageReadFunction) cbs_ ## codec ## _read_  ## name, \
> +    .write = (SEIMessageWriteFunction)cbs_ ## codec ## _write_ ## name
> +
> +// End-of-list sentinel element.
> +#define SEI_MESSAGE_TYPE_END { .type = -1 }
> +
> +
> +/**
> + * Find the type descriptor for the given payload type.
> + *
> + * Returns NULL if the payload type is not known.
> + */
> +const SEIMessageTypeDescriptor
> *ff_cbs_sei_find_type(CodedBitstreamContext *ctx,
> +                                                     int payload_type);
> +
> +/**
> + * Allocate a new payload for the given SEI message.
> + */
> +int ff_cbs_sei_alloc_message_payload(SEIRawMessage *message,
> +                                     const SEIMessageTypeDescriptor
> *desc);
> +
> +/**
> + * Allocate a new empty SEI message in a message list.
> + *
> + * The new message is in place nb_messages - 1.
> + */
> +int ff_cbs_sei_list_add(SEIRawMessageList *list);
> +
> +/**
> + * Free all SEI messages in a message list.
> + */
> +void ff_cbs_sei_free_message_list(SEIRawMessageList *list);
> +
> +/**
> + * Add an SEI message to an access unit.
> + *
> + * Will add to an existing SEI NAL unit, or create a new one for the
> + * message if there is no suitable existing one.
> + *
> + * Takes a new reference to payload_buf, if set.  If payload_buf is
> + * NULL then the new message will not be reference counted.
> + */
> +int ff_cbs_sei_add_message(CodedBitstreamContext *ctx,
> +                           CodedBitstreamFragment *au,
> +                           int prefix,
> +                           uint32_t     payload_type,
> +                           void        *payload_data,
> +                           AVBufferRef *payload_buf);
> +
> +/**
> + * Iterate over messages with the given payload type in an access unit.
> + *
> + * Set message to NULL in the first call.  Returns 0 while more messages
> + * are available, AVERROR(ENOENT) when all messages have been found.
> + */
> +int ff_cbs_sei_find_message(CodedBitstreamContext *ctx,
> +                            CodedBitstreamFragment *au,
> +                            uint32_t payload_type,
> +                            SEIRawMessage **message);
> +
> +/**
> + * Delete all messages with the given payload type from an access unit.
> + */
> +void ff_cbs_sei_delete_message_type(CodedBitstreamContext *ctx,
> +                                    CodedBitstreamFragment *au,
> +                                    uint32_t payload_type);
> +
>  #endif /* AVCODEC_CBS_SEI_H */
> diff --git a/libavcodec/cbs_sei_syntax_template.c
> b/libavcodec/cbs_sei_syntax_template.c
> index cc900830ae..1a0516acce 100644
> --- a/libavcodec/cbs_sei_syntax_template.c
> +++ b/libavcodec/cbs_sei_syntax_template.c
> @@ -16,9 +16,27 @@
>   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> 02110-1301 USA
>   */
>
> -static int FUNC(sei_user_data_registered)
> +static int FUNC(filler_payload)
>      (CodedBitstreamContext *ctx, RWContext *rw,
> -     SEIRawUserDataRegistered *current, uint32_t *payload_size)
> +     SEIRawFillerPayload *current, SEIMessageState *state)
> +{
> +    int err, i;
> +
> +    HEADER("Filler Payload");
> +
> +#ifdef READ
> +    current->payload_size = state->payload_size;
> +#endif
> +
> +    for (i = 0; i < current->payload_size; i++)
> +        fixed(8, ff_byte, 0xff);
> +
> +    return 0;
> +}
> +
> +static int FUNC(user_data_registered)
> +    (CodedBitstreamContext *ctx, RWContext *rw,
> +     SEIRawUserDataRegistered *current, SEIMessageState *state)
>  {
>      int err, i, j;
>
> @@ -33,14 +51,12 @@ static int FUNC(sei_user_data_registered)
>      }
>
>  #ifdef READ
> -    if (*payload_size < i) {
> +    if (state->payload_size < i) {
>          av_log(ctx->log_ctx, AV_LOG_ERROR,
>                 "Invalid SEI user data registered payload.\n");
>          return AVERROR_INVALIDDATA;
>      }
> -    current->data_length = *payload_size - i;
> -#else
> -    *payload_size = i + current->data_length;
> +    current->data_length = state->payload_size - i;
>  #endif
>
>      allocate(current->data, current->data_length);
> @@ -50,23 +66,21 @@ static int FUNC(sei_user_data_registered)
>      return 0;
>  }
>
> -static int FUNC(sei_user_data_unregistered)
> +static int FUNC(user_data_unregistered)
>      (CodedBitstreamContext *ctx, RWContext *rw,
> -     SEIRawUserDataUnregistered *current, uint32_t *payload_size)
> +     SEIRawUserDataUnregistered *current, SEIMessageState *state)
>  {
>      int err, i;
>
>      HEADER("User Data Unregistered");
>
>  #ifdef READ
> -    if (*payload_size < 16) {
> +    if (state->payload_size < 16) {
>          av_log(ctx->log_ctx, AV_LOG_ERROR,
>                 "Invalid SEI user data unregistered payload.\n");
>          return AVERROR_INVALIDDATA;
>      }
> -    current->data_length = *payload_size - 16;
> -#else
> -    *payload_size = 16 + current->data_length;
> +    current->data_length = state->payload_size - 16;
>  #endif
>
>      for (i = 0; i < 16; i++)
> @@ -80,9 +94,9 @@ static int FUNC(sei_user_data_unregistered)
>      return 0;
>  }
>
> -static int FUNC(sei_mastering_display_colour_volume)
> +static int FUNC(mastering_display_colour_volume)
>      (CodedBitstreamContext *ctx, RWContext *rw,
> -     SEIRawMasteringDisplayColourVolume *current)
> +     SEIRawMasteringDisplayColourVolume *current, SEIMessageState *state)
>  {
>      int err, c;
>
> @@ -104,13 +118,13 @@ static int FUNC(sei_mastering_display_colour_volume)
>      return 0;
>  }
>
> -static int FUNC(sei_content_light_level)
> +static int FUNC(content_light_level_info)
>      (CodedBitstreamContext *ctx, RWContext *rw,
> -     SEIRawContentLightLevelInfo *current)
> +     SEIRawContentLightLevelInfo *current, SEIMessageState *state)
>  {
>      int err;
>
> -    HEADER("Content Light Level");
> +    HEADER("Content Light Level Information");
>
>      ub(16, max_content_light_level);
>      ub(16, max_pic_average_light_level);
> @@ -118,9 +132,10 @@ static int FUNC(sei_content_light_level)
>      return 0;
>  }
>
> -static int FUNC(sei_alternative_transfer_characteristics)
> +static int FUNC(alternative_transfer_characteristics)
>      (CodedBitstreamContext *ctx, RWContext *rw,
> -     SEIRawAlternativeTransferCharacteristics *current)
> +     SEIRawAlternativeTransferCharacteristics *current,
> +     SEIMessageState *state)
>  {
>      int err;
>
> @@ -130,3 +145,165 @@ static int
> FUNC(sei_alternative_transfer_characteristics)
>
>      return 0;
>  }
> +
> +static int FUNC(message)(CodedBitstreamContext *ctx, RWContext *rw,
> +                         SEIRawMessage *current)
> +{
> +    const SEIMessageTypeDescriptor *desc;
> +    int err, i;
> +
> +    desc = ff_cbs_sei_find_type(ctx, current->payload_type);
> +    if (desc) {
> +        SEIMessageState 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_sei_alloc_message_payload(current, desc));
> +#endif
> +
> +        start_position = bit_position(rw);
> +
> +        CHECK(desc->READWRITE(ctx, rw, current->payload, &state));
> +
> +        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 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, bit_equal_to_one, 1);
> +            while (byte_alignment(rw))
> +                fixed(1, bit_equal_to_zero, 0);
> +        }
> +
> +#ifdef WRITE
> +        current->payload_size = (put_bits_count(rw) - start_position) / 8;
> +#endif
> +    } else {
> +        uint8_t *data;
> +
> +        allocate(current->payload, current->payload_size);
> +        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(message_list)(CodedBitstreamContext *ctx, RWContext *rw,
> +                              SEIRawMessageList *current, int prefix)
> +{
> +    SEIRawMessage *message;
> +    int err, k;
> +
> +#ifdef READ
> +    for (k = 0;; k++) {
> +        uint32_t payload_type = 0;
> +        uint32_t payload_size = 0;
> +        uint32_t tmp;
> +
> +        while (show_bits(rw, 8) == 0xff) {
> +            fixed(8, ff_byte, 0xff);
> +            payload_type += 255;
> +        }
> +        xu(8, last_payload_type_byte, tmp, 0, 254, 0);
> +        payload_type += tmp;
> +
> +        while (show_bits(rw, 8) == 0xff) {
> +            fixed(8, ff_byte, 0xff);
> +            payload_size += 255;
> +        }
> +        xu(8, last_payload_size_byte, tmp, 0, 254, 0);
> +        payload_size += tmp;
> +
> +        CHECK(ff_cbs_sei_list_add(current));
> +        message = &current->messages[k];
> +
> +        message->payload_type = payload_type;
> +        message->payload_size = payload_size;
> +
> +        CHECK(FUNC(message)(ctx, rw, message));
> +
> +        if (!cbs_h2645_read_more_rbsp_data(rw))
> +            break;
> +    }
> +#else
> +    for (k = 0; k < current->nb_messages; k++) {
> +        PutBitContext start_state;
> +        uint32_t tmp;
> +        int trace, i;
> +
> +        message = &current->messages[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 = message->payload_type;
> +            while (tmp >= 255) {
> +                fixed(8, ff_byte, 0xff);
> +                tmp -= 255;
> +            }
> +            xu(8, last_payload_type_byte, tmp, 0, 254, 0);
> +
> +            tmp = message->payload_size;
> +            while (tmp >= 255) {
> +                fixed(8, ff_byte, 0xff);
> +                tmp -= 255;
> +            }
> +            xu(8, last_payload_size_byte, tmp, 0, 254, 0);
> +
> +            err = FUNC(message)(ctx, rw, message);
> +            ctx->trace_enable = trace;
> +            if (err < 0)
> +                return err;
> +        }
> +    }
> +#endif
> +
> +    return 0;
> +}
> diff --git a/libavcodec/h264_metadata_bsf.c
> b/libavcodec/h264_metadata_bsf.c
> index f39e649ac6..4ab97aee3a 100644
> --- a/libavcodec/h264_metadata_bsf.c
> +++ b/libavcodec/h264_metadata_bsf.c
> @@ -78,13 +78,14 @@ typedef struct H264MetadataContext {
>      int crop_bottom;
>
>      const char *sei_user_data;
> -    H264RawSEIPayload sei_user_data_payload;
> +    SEIRawUserDataUnregistered sei_user_data_payload;
>
>      int delete_filler;
>
>      int display_orientation;
>      double rotate;
>      int flip;
> +    H264RawSEIDisplayOrientation display_orientation_payload;
>
>      int level;
>  } H264MetadataContext;
> @@ -414,7 +415,9 @@ static int h264_metadata_filter(AVBSFContext *bsf,
> AVPacket *pkt)
>      // Only insert the SEI in access units containing SPSs, and also
>      // unconditionally in the first access unit we ever see.
>      if (ctx->sei_user_data && (has_sps || !ctx->done_first_au)) {
> -        err = ff_cbs_h264_add_sei_message(au,
> &ctx->sei_user_data_payload);
> +        err = ff_cbs_sei_add_message(ctx->output, au, 1,
> +                                     H264_SEI_TYPE_USER_DATA_UNREGISTERED,
> +                                     &ctx->sei_user_data_payload, NULL);
>          if (err < 0) {
>              av_log(bsf, AV_LOG_ERROR, "Failed to add user data SEI "
>                     "message to access unit.\n");
> @@ -428,74 +431,54 @@ static int h264_metadata_filter(AVBSFContext *bsf,
> AVPacket *pkt)
>                  ff_cbs_delete_unit(au, i);
>                  continue;
>              }
> -
> -            if (au->units[i].type == H264_NAL_SEI) {
> -                // Filler SEI messages.
> -                H264RawSEI *sei = au->units[i].content;
> -
> -                for (j = sei->payload_count - 1; j >= 0; j--) {
> -                    if (sei->payload[j].payload_type ==
> -                        H264_SEI_TYPE_FILLER_PAYLOAD)
> -                        ff_cbs_h264_delete_sei_message(au, &au->units[i],
> j);
> -                }
> -            }
>          }
> +
> +        ff_cbs_sei_delete_message_type(ctx->output, au,
> +                                       H264_SEI_TYPE_FILLER_PAYLOAD);
>      }
>
>      if (ctx->display_orientation != PASS) {
> -        for (i = au->nb_units - 1; i >= 0; i--) {
> -            H264RawSEI *sei;
> -            if (au->units[i].type != H264_NAL_SEI)
> -                continue;
> -            sei = au->units[i].content;
> -
> -            for (j = sei->payload_count - 1; j >= 0; j--) {
> -                H264RawSEIDisplayOrientation *disp;
> -                int32_t *matrix;
> -
> -                if (sei->payload[j].payload_type !=
> -                    H264_SEI_TYPE_DISPLAY_ORIENTATION)
> -                    continue;
> -                disp = &sei->payload[j].payload.display_orientation;
> -
> -                if (ctx->display_orientation == REMOVE ||
> -                    ctx->display_orientation == INSERT) {
> -                    ff_cbs_h264_delete_sei_message(au, &au->units[i], j);
> -                    continue;
> -                }
> -
> -                matrix = av_malloc(9 * sizeof(int32_t));
> -                if (!matrix) {
> -                    err = AVERROR(ENOMEM);
> -                    goto fail;
> -                }
> +        SEIRawMessage *message = NULL;
> +        while (ff_cbs_sei_find_message(ctx->output, au,
> +                                       H264_SEI_TYPE_DISPLAY_ORIENTATION,
> +                                       &message) == 0) {
> +            H264RawSEIDisplayOrientation *disp = message->payload;
> +            int32_t *matrix;
> +
> +            matrix = av_malloc(9 * sizeof(int32_t));
> +            if (!matrix) {
> +                err = AVERROR(ENOMEM);
> +                goto fail;
> +            }
>
> -                av_display_rotation_set(matrix,
> -                                        disp->anticlockwise_rotation *
> -                                        180.0 / 65536.0);
> -                av_display_matrix_flip(matrix, disp->hor_flip,
> disp->ver_flip);
> -
> -                // If there are multiple display orientation messages in
> an
> -                // access unit, then the last one added to the packet
> (i.e.
> -                // the first one in the access unit) will prevail.
> -                err = av_packet_add_side_data(pkt,
> AV_PKT_DATA_DISPLAYMATRIX,
> -                                              (uint8_t*)matrix,
> -                                              9 * sizeof(int32_t));
> -                if (err < 0) {
> -                    av_log(bsf, AV_LOG_ERROR, "Failed to attach extracted
> "
> -                           "displaymatrix side data to packet.\n");
> -                    av_free(matrix);
> -                    goto fail;
> -                }
> +            av_display_rotation_set(matrix,
> +                                    disp->anticlockwise_rotation *
> +                                    180.0 / 65536.0);
> +            av_display_matrix_flip(matrix, disp->hor_flip,
> disp->ver_flip);
> +
> +            // If there are multiple display orientation messages in an
> +            // access unit, then the last one added to the packet (i.e.
> +            // the first one in the access unit) will prevail.
> +            err = av_packet_add_side_data(pkt, AV_PKT_DATA_DISPLAYMATRIX,
> +                                          (uint8_t*)matrix,
> +                                          9 * sizeof(int32_t));
> +            if (err < 0) {
> +                av_log(bsf, AV_LOG_ERROR, "Failed to attach extracted "
> +                       "displaymatrix side data to packet.\n");
> +                av_free(matrix);
> +                goto fail;
>              }
>          }
> +
> +        if (ctx->display_orientation == REMOVE ||
> +            ctx->display_orientation == INSERT) {
> +            ff_cbs_sei_delete_message_type(ctx->output, au,
> +
>  H264_SEI_TYPE_DISPLAY_ORIENTATION);
> +        }
>      }
>      if (ctx->display_orientation == INSERT) {
> -        H264RawSEIPayload payload = {
> -            .payload_type = H264_SEI_TYPE_DISPLAY_ORIENTATION,
> -        };
>          H264RawSEIDisplayOrientation *disp =
> -            &payload.payload.display_orientation;
> +            &ctx->display_orientation_payload;
>          uint8_t *data;
>          int size;
>          int write = 0;
> @@ -551,7 +534,9 @@ static int h264_metadata_filter(AVBSFContext *bsf,
> AVPacket *pkt)
>          if (write) {
>              disp->display_orientation_repetition_period = 1;
>
> -            err = ff_cbs_h264_add_sei_message(au, &payload);
> +            err = ff_cbs_sei_add_message(ctx->output, au, 1,
> +
>  H264_SEI_TYPE_DISPLAY_ORIENTATION,
> +                                         disp, NULL);
>              if (err < 0) {
>                  av_log(bsf, AV_LOG_ERROR, "Failed to add display
> orientation "
>                         "SEI message to access unit.\n");
> @@ -585,13 +570,9 @@ static int h264_metadata_init(AVBSFContext *bsf)
>      int err, i;
>
>      if (ctx->sei_user_data) {
> -        SEIRawUserDataUnregistered *udu =
> -            &ctx->sei_user_data_payload.payload.user_data_unregistered;
> +        SEIRawUserDataUnregistered *udu = &ctx->sei_user_data_payload;
>          int j;
>
> -        ctx->sei_user_data_payload.payload_type =
> -            H264_SEI_TYPE_USER_DATA_UNREGISTERED;
> -
>          // Parse UUID.  It must be a hex string of length 32, possibly
>          // containing '-'s between hex digits (which we ignore).
>          for (i = j = 0; j < 32 && i < 64 && ctx->sei_user_data[i]; i++) {
> diff --git a/libavcodec/vaapi_encode_h264.c
> b/libavcodec/vaapi_encode_h264.c
> index b577d09caf..d24462414c 100644
> --- a/libavcodec/vaapi_encode_h264.c
> +++ b/libavcodec/vaapi_encode_h264.c
> @@ -90,7 +90,6 @@ typedef struct VAAPIEncodeH264Context {
>      H264RawAUD   raw_aud;
>      H264RawSPS   raw_sps;
>      H264RawPPS   raw_pps;
> -    H264RawSEI   raw_sei;
>      H264RawSlice raw_slice;
>
>      H264RawSEIBufferingPeriod      sei_buffering_period;
> @@ -210,11 +209,9 @@ static int
> vaapi_encode_h264_write_extra_header(AVCodecContext *avctx,
>  {
>      VAAPIEncodeH264Context *priv = avctx->priv_data;
>      CodedBitstreamFragment   *au = &priv->current_access_unit;
> -    int err, i;
> +    int err;
>
>      if (priv->sei_needed) {
> -        H264RawSEI *sei = &priv->raw_sei;
> -
>          if (priv->aud_needed) {
>              err = vaapi_encode_h264_add_nal(avctx, au, &priv->raw_aud);
>              if (err < 0)
> @@ -222,41 +219,35 @@ static int
> vaapi_encode_h264_write_extra_header(AVCodecContext *avctx,
>              priv->aud_needed = 0;
>          }
>
> -        *sei = (H264RawSEI) {
> -            .nal_unit_header = {
> -                .nal_unit_type = H264_NAL_SEI,
> -            },
> -        };
> -
> -        i = 0;
> -
>          if (priv->sei_needed & SEI_IDENTIFIER) {
> -            sei->payload[i].payload_type =
> H264_SEI_TYPE_USER_DATA_UNREGISTERED;
> -            sei->payload[i].payload.user_data_unregistered =
> priv->sei_identifier;
> -            ++i;
> +            err = ff_cbs_sei_add_message(priv->cbc, au, 1,
> +
>  H264_SEI_TYPE_USER_DATA_UNREGISTERED,
> +                                         &priv->sei_identifier, NULL);
> +            if (err < 0)
> +                goto fail;
>          }
>          if (priv->sei_needed & SEI_TIMING) {
>              if (pic->type == PICTURE_TYPE_IDR) {
> -                sei->payload[i].payload_type =
> H264_SEI_TYPE_BUFFERING_PERIOD;
> -                sei->payload[i].payload.buffering_period =
> priv->sei_buffering_period;
> -                ++i;
> +                err = ff_cbs_sei_add_message(priv->cbc, au, 1,
> +
>  H264_SEI_TYPE_BUFFERING_PERIOD,
> +                                             &priv->sei_buffering_period,
> NULL);
> +                if (err < 0)
> +                    goto fail;
>              }
> -            sei->payload[i].payload_type = H264_SEI_TYPE_PIC_TIMING;
> -            sei->payload[i].payload.pic_timing = priv->sei_pic_timing;
> -            ++i;
> +            err = ff_cbs_sei_add_message(priv->cbc, au, 1,
> +                                         H264_SEI_TYPE_PIC_TIMING,
> +                                         &priv->sei_pic_timing, NULL);
> +            if (err < 0)
> +                goto fail;
>          }
>          if (priv->sei_needed & SEI_RECOVERY_POINT) {
> -            sei->payload[i].payload_type = H264_SEI_TYPE_RECOVERY_POINT;
> -            sei->payload[i].payload.recovery_point =
> priv->sei_recovery_point;
> -            ++i;
> +            err = ff_cbs_sei_add_message(priv->cbc, au, 1,
> +                                         H264_SEI_TYPE_RECOVERY_POINT,
> +                                         &priv->sei_recovery_point, NULL);
> +            if (err < 0)
> +                goto fail;
>          }
>
> -        sei->payload_count = i;
> -        av_assert0(sei->payload_count > 0);
> -
> -        err = vaapi_encode_h264_add_nal(avctx, au, sei);
> -        if (err < 0)
> -            goto fail;
>          priv->sei_needed = 0;
>
>          err = vaapi_encode_h264_write_access_unit(avctx, data, data_len,
> au);
> diff --git a/libavcodec/vaapi_encode_h265.c
> b/libavcodec/vaapi_encode_h265.c
> index a7af763ae4..2e8e772008 100644
> --- a/libavcodec/vaapi_encode_h265.c
> +++ b/libavcodec/vaapi_encode_h265.c
> @@ -73,7 +73,6 @@ typedef struct VAAPIEncodeH265Context {
>      H265RawVPS   raw_vps;
>      H265RawSPS   raw_sps;
>      H265RawPPS   raw_pps;
> -    H265RawSEI   raw_sei;
>      H265RawSlice raw_slice;
>
>      SEIRawMasteringDisplayColourVolume sei_mastering_display;
> @@ -195,11 +194,9 @@ static int
> vaapi_encode_h265_write_extra_header(AVCodecContext *avctx,
>  {
>      VAAPIEncodeH265Context *priv = avctx->priv_data;
>      CodedBitstreamFragment   *au = &priv->current_access_unit;
> -    int err, i;
> +    int err;
>
>      if (priv->sei_needed) {
> -        H265RawSEI *sei = &priv->raw_sei;
> -
>          if (priv->aud_needed) {
>              err = vaapi_encode_h265_add_nal(avctx, au, &priv->aud);
>              if (err < 0)
> @@ -207,35 +204,22 @@ static int
> vaapi_encode_h265_write_extra_header(AVCodecContext *avctx,
>              priv->aud_needed = 0;
>          }
>
> -        *sei = (H265RawSEI) {
> -            .nal_unit_header = {
> -                .nal_unit_type         = HEVC_NAL_SEI_PREFIX,
> -                .nuh_layer_id          = 0,
> -                .nuh_temporal_id_plus1 = 1,
> -            },
> -        };
> -
> -        i = 0;
> -
>          if (priv->sei_needed & SEI_MASTERING_DISPLAY) {
> -            sei->payload[i].payload_type =
> HEVC_SEI_TYPE_MASTERING_DISPLAY_INFO;
> -            sei->payload[i].payload.mastering_display_colour_volume =
> -                priv->sei_mastering_display;
> -            ++i;
> +            err = ff_cbs_sei_add_message(priv->cbc, au, 1,
> +
>  HEVC_SEI_TYPE_MASTERING_DISPLAY_INFO,
> +                                         &priv->sei_mastering_display,
> NULL);
> +            if (err < 0)
> +                goto fail;
>          }
>
>          if (priv->sei_needed & SEI_CONTENT_LIGHT_LEVEL) {
> -            sei->payload[i].payload_type =
> HEVC_SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO;
> -            sei->payload[i].payload.content_light_level =
> priv->sei_content_light_level;
> -            ++i;
> +            err = ff_cbs_sei_add_message(priv->cbc, au, 1,
> +
>  HEVC_SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO,
> +                                         &priv->sei_content_light_level,
> NULL);
> +            if (err < 0)
> +                goto fail;
>          }
>
> -        sei->payload_count = i;
> -        av_assert0(sei->payload_count > 0);
> -
> -        err = vaapi_encode_h265_add_nal(avctx, au, sei);
> -        if (err < 0)
> -            goto fail;
>          priv->sei_needed = 0;
>
>          err = vaapi_encode_h265_write_access_unit(avctx, data, data_len,
> au);
> --
> 2.29.2
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Mark Thompson Jan. 6, 2021, 6:57 p.m. UTC | #2
On 04/01/2021 15:42, Nuo Mi wrote:
> On Sat, Jan 2, 2021 at 5:44 AM Mark Thompson <sw@jkqxz.net> wrote:
> 
>> ---
>>   libavcodec/Makefile                   |   4 +-
>>   libavcodec/cbs_h264.h                 |  50 +---
>>   libavcodec/cbs_h2645.c                | 300 +++++++++++----------
>>   libavcodec/cbs_h264_syntax_template.c | 173 +-----------
>>   libavcodec/cbs_h265.h                 |  33 +--
>>   libavcodec/cbs_h265_syntax_template.c | 269 +++----------------
>>   libavcodec/cbs_sei.c                  | 369 ++++++++++++++++++++++++++
>>   libavcodec/cbs_sei.h                  | 255 ++++++++++++++++++
>>   libavcodec/cbs_sei_syntax_template.c  | 215 +++++++++++++--
>>   libavcodec/h264_metadata_bsf.c        | 113 ++++----
>>   libavcodec/vaapi_encode_h264.c        |  51 ++--
>>   libavcodec/vaapi_encode_h265.c        |  38 +--
>>   12 files changed, 1107 insertions(+), 763 deletions(-)
>>   create mode 100644 libavcodec/cbs_sei.c
>>
>> ...
>> diff --git a/libavcodec/cbs_sei.c b/libavcodec/cbs_sei.c
>> new file mode 100644
>> index 0000000000..323997b600
>> --- /dev/null
>> +++ b/libavcodec/cbs_sei.c
>> @@ -0,0 +1,369 @@
>> +/*
>> + * 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 "cbs.h"
>> +#include "cbs_internal.h"
>> +#include "cbs_h264.h"
>> +#include "cbs_h265.h"
>> +#include "cbs_sei.h"
>> +
>> +static void cbs_free_user_data_registered(void *opaque, uint8_t *data)
>> +{
>> +    SEIRawUserDataRegistered *udr = (SEIRawUserDataRegistered*)data;
>> +    av_buffer_unref(&udr->data_ref);
>> +    av_free(udr);
>> +}
>> +
>> +static void cbs_free_user_data_unregistered(void *opaque, uint8_t *data)
>> +{
>> +    SEIRawUserDataUnregistered *udu = (SEIRawUserDataUnregistered*)data;
>> +    av_buffer_unref(&udu->data_ref);
>> +    av_free(udu);
>> +}
>> +
>> +int ff_cbs_sei_alloc_message_payload(SEIRawMessage *message,
>> +                                     const SEIMessageTypeDescriptor *desc)
>> +{
>> +    void (*free_func)(void*, uint8_t*);
>> +
>> +    av_assert0(message->payload     == NULL &&
>> +               message->payload_ref == NULL);
>> +    message->payload_type = desc->type;
>> +
>> +    if (desc->type == SEI_TYPE_USER_DATA_REGISTERED_ITU_T_T35)
>> +        free_func = &cbs_free_user_data_registered;
>> +    else if (desc->type == SEI_TYPE_USER_DATA_UNREGISTERED)
>> +        free_func = &cbs_free_user_data_unregistered;
>> +    else
>> +        free_func = NULL;
>> +
>> +    if (free_func) {
>> +        message->payload = av_mallocz(desc->size);
>> +        if (!message->payload)
>> +            return AVERROR(ENOMEM);
>> +        message->payload_ref =
>> +            av_buffer_create(message->payload, desc->size,
>> +                             free_func, NULL, 0);
>>
> leak  message->payload if av_buffer_create returns error

Fixed.

>> +    } else {
>> +        message->payload_ref = av_buffer_alloc(desc->size);
>> +    }
>> +    if (!message->payload_ref)
>> +        return AVERROR(ENOMEM);
>> +    message->payload = message->payload_ref->data;
>> +
>> +    return 0;
>> +}
>> +
>> +int ff_cbs_sei_list_add(SEIRawMessageList *list)
>> +{
>> +    void *ptr;
>> +    int old_count = list->nb_messages_allocated;
>> +
>> +    av_assert0(list->nb_messages <= old_count);
>> +    if (list->nb_messages + 1 > old_count) {
>> +        int new_count = 2 * old_count + 1;
>> +
>> +        ptr = av_realloc_array(list->messages,
>> +                               new_count, sizeof(*list->messages));
>> +        if (!ptr)
>> +            return AVERROR(ENOMEM);
>> +
>> +        list->messages = ptr;
>> +        list->nb_messages_allocated = new_count;
>> +
>> +        // Zero the newly-added entries.
>> +        memset(list->messages + old_count, 0,
>> +               (new_count - old_count) * sizeof(*list->messages));
>> +    }
>> +    ++list->nb_messages;
>> +    return 0;
>> +}
>> +
>> +void ff_cbs_sei_free_message_list(SEIRawMessageList *list)
>> +{
>> +    for (int i = 0; i < list->nb_messages; i++) {
>> +        SEIRawMessage *message = &list->messages[i];
>> +        av_buffer_unref(&message->payload_ref);
>> +        av_buffer_unref(&message->extension_data_ref);
>> +    }
>> +    av_free(list->messages);
>> +}
>> +
>> +static int cbs_sei_get_unit(CodedBitstreamContext *ctx,
>> +                            CodedBitstreamFragment *au,
>> +                            int prefix,
>> +                            CodedBitstreamUnit **sei_unit)
>> +{
>> +    CodedBitstreamUnit *unit;
>> +    int sei_type, highest_vcl_type, err, i, position;
>> +
>> +    switch (ctx->codec->codec_id) {
>> +    case AV_CODEC_ID_H264:
>> +        // (We can ignore auxiliary slices because we only have prefix
>> +        // SEI in H.264 and an auxiliary picture must always follow a
>> +        // primary picture.)
>> +        highest_vcl_type = H264_NAL_IDR_SLICE;
>> +        if (prefix)
>> +            sei_type = H264_NAL_SEI;
>> +        else
>> +            return AVERROR(EINVAL);
>> +        break;
>> +    case AV_CODEC_ID_H265:
>> +        highest_vcl_type = HEVC_NAL_RSV_VCL31;
>> +        if (prefix)
>> +            sei_type = HEVC_NAL_SEI_PREFIX;
>> +        else
>> +            sei_type = HEVC_NAL_SEI_SUFFIX;
>> +        break;
>> +    default:
>> +        return AVERROR(EINVAL);
>> +    }
>> +
>> +    // Find an existing SEI NAL unit of the right type.
>> +    unit = NULL;
>> +    for (i = 0; i < au->nb_units; i++) {
>> +        if (au->units[i].type == sei_type) {
>> +            unit = &au->units[i];
>> +            break;
>> +        }
>> +    }
>> +
>> +    if (unit) {
>> +        *sei_unit = unit;
>> +        return 0;
>> +    }
>> +
>> +    // Need to add a new SEI NAL unit ...
>> +    if (prefix) {
>> +        // ... before the first VCL NAL unit.
>> +        for (i = 0; i < au->nb_units; i++) {
>> +            if (au->units[i].type < highest_vcl_type)
>> +                break;
>> +        }
>> +        position = i;
>> +    } else {
>> +        // ... after the last VCL NAL unit.
>> +        for (i = au->nb_units; i >= 0; i--) {
>>
> This will access au->units[au->nb_units], will it read beyond the boundary?

Yes, fixed.

(This code is currently unreachable, because it never tries to insert anything which would be suffix SEI.  I'll try hacking something in temporarily to test it.)

>> +            if (au->units[i].type < highest_vcl_type)
>> +                break;
>> +        }
>> +        if (i < 0) {
>> +            // No VCL units; just put it at the end.
>> +            position = -1;
>> +        } else {
>> +            position = i + 1;
>> +        }
>> +    }
>> +
>> +    err = ff_cbs_insert_unit_content(au, position, sei_type,
>> +                                     NULL, NULL);
>> +    if (err < 0)
>> +        return err;
>> +    unit = &au->units[position];
>> +    unit->type = sei_type;
>> +
>> +    err = ff_cbs_alloc_unit_content2(ctx, unit);
>> +    if (err < 0)
>> +        return err;
>> +
>> +    switch (ctx->codec->codec_id) {
>> +    case AV_CODEC_ID_H264:
>> +        {
>> +            H264RawSEI sei = {
>> +                .nal_unit_header = {
>> +                    .nal_ref_idc   = 0,
>> +                    .nal_unit_type = sei_type,
>> +                },
>> +            };
>> +            memcpy(unit->content, &sei, sizeof(sei));
>> +        }
>> +        break;
>> +    case AV_CODEC_ID_H265:
>> +        {
>> +            H265RawSEI 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);
>> +    }
>> +
>> +    *sei_unit = unit;
>> +    return 0;
>> +}
>> +
>> ...
>> +
>> +void ff_cbs_sei_delete_message_type(CodedBitstreamContext *ctx,
>> +                                    CodedBitstreamFragment *au,
>> +                                    uint32_t payload_type)
>> +{
>> +    int err, i, j;
>> +
>> +    for (i = 0; i < au->nb_units; i++) {
>> +        CodedBitstreamUnit *unit = &au->units[i];
>> +        SEIRawMessageList *list;
>> +
>> +        err = cbs_sei_get_message_list(ctx, unit, &list);
>> +        if (err < 0)
>> +            continue;
>> +
>> +        for (j = 0; j < list->nb_messages;) {
>> +            if (list->messages[j].payload_type == payload_type)
>> +                cbs_sei_delete_message(list, j);
>> +            else
>> +                ++j;
>> +        }
>>
> loop from end to start may be better from performance and code size aspect.

Not really performance sensitive since it's unlikely there is more than one of any message type, but it also doesn't cost anything so changed.

>> +    }
>> +}
>> ...
>> +
>> +// SEI payload types form a common namespace between the H.264, H.265
>> +// and H.266 standards.  A given payload type always has the same
>> +// meaning, but some names have different payload types in different
>> +// standards (e.g. scalable-nesting is 30 in H.264 but 133 in H.265).
>> +// The content of the payload data depends on the standard, though
>> +// many generic parts have the same interpretation everywhere (such as
>> +// mastering-display-colour-volume and user-data-unregistered).
>> +enum {
>> +    SEI_TYPE_BUFFERING_PERIOD                            = 0,
>> +    SEI_TYPE_PIC_TIMING                                  = 1,
>> +    SEI_TYPE_PAN_SCAN_RECT                               = 2,
>> +    SEI_TYPE_FILLER_PAYLOAD                              = 3,
>> +    SEI_TYPE_USER_DATA_REGISTERED_ITU_T_T35              = 4,
>> +    SEI_TYPE_USER_DATA_UNREGISTERED                      = 5,
>> +    SEI_TYPE_RECOVERY_POINT                              = 6,
>> +    SEI_TYPE_DEC_REF_PIC_MARKING_REPETITION              = 7,
>> +    SEI_TYPE_SPARE_PIC                                   = 8,
>> +    SEI_TYPE_SCENE_INFO                                  = 9,
>> +    SEI_TYPE_SUB_SEQ_INFO                                = 10,
>> +    SEI_TYPE_SUB_SEQ_LAYER_CHARACTERISTICS               = 11,
>> +    SEI_TYPE_SUB_SEQ_CHARACTERISTICS                     = 12,
>> +    SEI_TYPE_FULL_FRAME_FREEZE                           = 13,
>> +    SEI_TYPE_FULL_FRAME_FREEZE_RELEASE                   = 14,
>> +    SEI_TYPE_FULL_FRAME_SNAPSHOT                         = 15,
>> +    SEI_TYPE_PROGRESSIVE_REFINEMENT_SEGMENT_START        = 16,
>> +    SEI_TYPE_PROGRESSIVE_REFINEMENT_SEGMENT_END          = 17,
>> +    SEI_TYPE_MOTION_CONSTRAINED_SLICE_GROUP_SET          = 18,
>> +    SEI_TYPE_FILM_GRAIN_CHARACTERISTICS                  = 19,
>> +    SEI_TYPE_DEBLOCKING_FILTER_DISPLAY_PREFERENCE        = 20,
>> +    SEI_TYPE_STEREO_VIDEO_INFO                           = 21,
>> +    SEI_TYPE_POST_FILTER_HINT                            = 22,
>> +    SEI_TYPE_TONE_MAPPING_INFO                           = 23,
>> +    SEI_TYPE_SCALABILITY_INFO                            = 24,
>> +    SEI_TYPE_SUB_PIC_SCALABLE_LAYER                      = 25,
>> +    SEI_TYPE_NON_REQUIRED_LAYER_REP                      = 26,
>> +    SEI_TYPE_PRIORITY_LAYER_INFO                         = 27,
>> +    SEI_TYPE_LAYERS_NOT_PRESENT_4                        = 28,
>> +    SEI_TYPE_LAYER_DEPENDENCY_CHANGE                     = 29,
>> +    SEI_TYPE_SCALABLE_NESTING_4                          = 30,
>> +    SEI_TYPE_BASE_LAYER_TEMPORAL_HRD                     = 31,
>> +    SEI_TYPE_QUALITY_LAYER_INTEGRITY_CHECK               = 32,
>> +    SEI_TYPE_REDUNDANT_PIC_PROPERTY                      = 33,
>> +    SEI_TYPE_TL0_DEP_REP_INDEX                           = 34,
>> +    SEI_TYPE_TL_SWITCHING_POINT                          = 35,
>> +    SEI_TYPE_PARALLEL_DECODING_INFO                      = 36,
>> +    SEI_TYPE_MVC_SCALABLE_NESTING                        = 37,
>> +    SEI_TYPE_VIEW_SCALABILITY_INFO                       = 38,
>> +    SEI_TYPE_MULTIVIEW_SCENE_INFO_4                      = 39,
>> +    SEI_TYPE_MULTIVIEW_ACQUISITION_INFO_4                = 40,
>> +    SEI_TYPE_NON_REQUIRED_VIEW_COMPONENT                 = 41,
>> +    SEI_TYPE_VIEW_DEPENDENCY_CHANGE                      = 42,
>> +    SEI_TYPE_OPERATION_POINTS_NOT_PRESENT                = 43,
>> +    SEI_TYPE_BASE_VIEW_TEMPORAL_HRD                      = 44,
>> +    SEI_TYPE_FRAME_PACKING_ARRANGEMENT                   = 45,
>> +    SEI_TYPE_MULTIVIEW_VIEW_POSITION_4                   = 46,
>> +    SEI_TYPE_DISPLAY_ORIENTATION                         = 47,
>> +    SEI_TYPE_MVCD_SCALABLE_NESTING                       = 48,
>> +    SEI_TYPE_MVCD_VIEW_SCALABILITY_INFO                  = 49,
>> +    SEI_TYPE_DEPTH_REPRESENTATION_INFO_4                 = 50,
>> +    SEI_TYPE_THREE_DIMENSIONAL_REFERENCE_DISPLAYS_INFO_4 = 51,
>> +    SEI_TYPE_DEPTH_TIMING                                = 52,
>> +    SEI_TYPE_DEPTH_SAMPLING_INFO                         = 53,
>> +    SEI_TYPE_CONSTRAINED_DEPTH_PARAMETER_SET_IDENTIFIER  = 54,
>> +    SEI_TYPE_GREEN_METADATA                              = 56,
>> +    SEI_TYPE_STRUCTURE_OF_PICTURES_INFO                  = 128,
>> +    SEI_TYPE_ACTIVE_PARAMETER_SETS                       = 129,
> 
> +    SEI_TYPE_DECODING_UNIT_INFO                          = 130,

Um, it's already there?  (And in <https://lists.ffmpeg.org/pipermail/ffmpeg-devel/2021-January/274208.html> - maybe you accidentally deleted it in your reply?)

>> +    SEI_TYPE_TEMPORAL_SUB_LAYER_ZERO_IDX                 = 131,
>> +    SEI_TYPE_DECODED_PICTURE_HASH                        = 132,
>> +    SEI_TYPE_SCALABLE_NESTING_5                          = 133,
>> +    SEI_TYPE_REGION_REFRESH_INFO                         = 134,
>> +    SEI_TYPE_NO_DISPLAY                                  = 135,
>> +    SEI_TYPE_TIME_CODE                                   = 136,
>> +    SEI_TYPE_MASTERING_DISPLAY_COLOUR_VOLUME             = 137,
>> +    SEI_TYPE_SEGMENTED_RECT_FRAME_PACKING_ARRANGEMENT    = 138,
>> +    SEI_TYPE_TEMPORAL_MOTION_CONSTRAINED_TILE_SETS       = 139,
>> +    SEI_TYPE_CHROMA_RESAMPLING_FILTER_HINT               = 140,
>> +    SEI_TYPE_KNEE_FUNCTION_INFO                          = 141,
>> +    SEI_TYPE_COLOUR_REMAPPING_INFO                       = 142,
>> +    SEI_TYPE_DEINTERLACED_FIELD_IDENTIFICATION           = 143,
>> +    SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO                    = 144,
>> +    SEI_TYPE_DEPENDENT_RAP_INDICATION                    = 145,
>> +    SEI_TYPE_CODED_REGION_COMPLETION                     = 146,
>> +    SEI_TYPE_ALTERNATIVE_TRANSFER_CHARACTERISTICS        = 147,
>> +    SEI_TYPE_AMBIENT_VIEWING_ENVIRONMENT                 = 148,
>> +    SEI_TYPE_CONTENT_COLOUR_VOLUME                       = 149,
>> +    SEI_TYPE_EQUIRECTANGULAR_PROJECTION                  = 150,
>> +    SEI_TYPE_CUBEMAP_PROJECTION                          = 151,
>> +    SEI_TYPE_FISHEYE_VIDEO_INFO                          = 152,
>> +    SEI_TYPE_SPHERE_ROTATION                             = 154,
>> +    SEI_TYPE_REGIONWISE_PACKING                          = 155,
>> +    SEI_TYPE_OMNI_VIEWPORT                               = 156,
>> +    SEI_TYPE_REGIONAL_NESTING                            = 157,
>> +    SEI_TYPE_MCTS_EXTRACTION_INFO_SETS                   = 158,
>> +    SEI_TYPE_MCTS_EXTRACTION_INFO_NESTING                = 159,
>> +    SEI_TYPE_LAYERS_NOT_PRESENT_5                        = 160,
>> +    SEI_TYPE_INTER_LAYER_CONSTRAINED_TILE_SETS           = 161,
>> +    SEI_TYPE_BSP_NESTING                                 = 162,
>> +    SEI_TYPE_BSP_INITIAL_ARRIVAL_TIME                    = 163,
>> +    SEI_TYPE_SUB_BITSTREAM_PROPERTY                      = 164,
>> +    SEI_TYPE_ALPHA_CHANNEL_INFO                          = 165,
>> +    SEI_TYPE_OVERLAY_INFO                                = 166,
>> +    SEI_TYPE_TEMPORAL_MV_PREDICTION_CONSTRAINTS          = 167,
>> +    SEI_TYPE_FRAME_FIELD_INFO                            = 168,
>> +    SEI_TYPE_THREE_DIMENSIONAL_REFERENCE_DISPLAYS_INFO   = 176,
>> +    SEI_TYPE_DEPTH_REPRESENTATION_INFO_5                 = 177,
>> +    SEI_TYPE_MULTIVIEW_SCENE_INFO_5                      = 178,
>> +    SEI_TYPE_MULTIVIEW_ACQUISITION_INFO_5                = 179,
>> +    SEI_TYPE_MULTIVIEW_VIEW_POSITION_5                   = 180,
>> +    SEI_TYPE_ALTERNATIVE_DEPTH_INFO                      = 181,
>> +    SEI_TYPE_SEI_MANIFEST                                = 200,
>> +    SEI_TYPE_SEI_PREFIX_INDICATION                       = 201,
>> +    SEI_TYPE_ANNOTATED_REGIONS                           = 202,
>> +    SEI_TYPE_SUBPIC_LEVEL_INFO                           = 203,
>> +    SEI_TYPE_SAMPLE_ASPECT_RATIO_INFO                    = 204,
>> +};
>> +
>> ...

On reviewing, it really helps to trim irrelevant context from replies so that we aren't searching through long messages looking for comments.  (And apologies if I missed anything.)

Thanks,

- Mark
Nuo Mi Jan. 7, 2021, 11:28 a.m. UTC | #3
>
> >> ...
>
> On reviewing, it really helps to trim irrelevant context from replies so
> that we aren't searching through long messages looking for comments.  (And
> apologies if I missed anything.)
>
No, you address all my concerns. thanks for the suggstion.


>
> Thanks,
>
> - Mark
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
diff mbox series

Patch

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 450781886d..6e12a8171d 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -71,8 +71,8 @@  OBJS-$(CONFIG_BSWAPDSP)                += bswapdsp.o
 OBJS-$(CONFIG_CABAC)                   += cabac.o
 OBJS-$(CONFIG_CBS)                     += cbs.o
 OBJS-$(CONFIG_CBS_AV1)                 += cbs_av1.o
-OBJS-$(CONFIG_CBS_H264)                += cbs_h2645.o h2645_parse.o
-OBJS-$(CONFIG_CBS_H265)                += cbs_h2645.o h2645_parse.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_JPEG)                += cbs_jpeg.o
 OBJS-$(CONFIG_CBS_MPEG2)               += cbs_mpeg2.o
 OBJS-$(CONFIG_CBS_VP9)                 += cbs_vp9.o
diff --git a/libavcodec/cbs_h264.h b/libavcodec/cbs_h264.h
index 81113f1ad0..9eb97eae24 100644
--- a/libavcodec/cbs_h264.h
+++ b/libavcodec/cbs_h264.h
@@ -291,34 +291,9 @@  typedef struct H264RawSEIDisplayOrientation {
     uint8_t display_orientation_extension_flag;
 } H264RawSEIDisplayOrientation;
 
-typedef struct H264RawSEIPayload {
-    uint32_t payload_type;
-    uint32_t payload_size;
-    union {
-        H264RawSEIBufferingPeriod buffering_period;
-        H264RawSEIPicTiming pic_timing;
-        H264RawSEIPanScanRect pan_scan_rect;
-        // H264RawSEIFiller filler -> no fields.
-        SEIRawUserDataRegistered user_data_registered;
-        SEIRawUserDataUnregistered user_data_unregistered;
-        H264RawSEIRecoveryPoint recovery_point;
-        H264RawSEIDisplayOrientation display_orientation;
-        SEIRawMasteringDisplayColourVolume mastering_display_colour_volume;
-        SEIRawAlternativeTransferCharacteristics
-            alternative_transfer_characteristics;
-        struct {
-            uint8_t     *data;
-            AVBufferRef *data_ref;
-            size_t       data_length;
-        } other;
-    } payload;
-} H264RawSEIPayload;
-
 typedef struct H264RawSEI {
     H264RawNALUnitHeader nal_unit_header;
-
-    H264RawSEIPayload payload[H264_MAX_SEI_PAYLOADS];
-    uint8_t payload_count;
+    SEIRawMessageList    message_list;
 } H264RawSEI;
 
 typedef struct H264RawSliceHeader {
@@ -438,27 +413,4 @@  typedef struct CodedBitstreamH264Context {
     uint8_t last_slice_nal_unit_type;
 } CodedBitstreamH264Context;
 
-
-/**
- * Add an SEI message to an access unit.
- *
- * On success, the payload will be owned by a unit in access_unit;
- * on failure, the content of the payload will be freed.
- */
-int ff_cbs_h264_add_sei_message(CodedBitstreamFragment *access_unit,
-                                H264RawSEIPayload *payload);
-
-/**
- * Delete an SEI message from an access unit.
- *
- * Deletes from nal_unit, which must be an SEI NAL unit.  If this is the
- * last message in nal_unit, also deletes it from access_unit.
- *
- * Requires nal_unit to be a unit in access_unit and position to be >= 0
- * and < the payload count of the SEI nal_unit.
- */
-void ff_cbs_h264_delete_sei_message(CodedBitstreamFragment *access_unit,
-                                    CodedBitstreamUnit *nal_unit,
-                                    int position);
-
 #endif /* AVCODEC_CBS_H264_H */
diff --git a/libavcodec/cbs_h2645.c b/libavcodec/cbs_h2645.c
index ce58e47a36..a00bc27370 100644
--- a/libavcodec/cbs_h2645.c
+++ b/libavcodec/cbs_h2645.c
@@ -348,6 +348,7 @@  static int cbs_h2645_read_more_rbsp_data(GetBitContext *gbc)
 
 #define more_rbsp_data(var) ((var) = cbs_h2645_read_more_rbsp_data(rw))
 
+#define bit_position(rw)   (get_bits_count(rw))
 #define byte_alignment(rw) (get_bits_count(rw) % 8)
 
 #define allocate(name, size) do { \
@@ -379,6 +380,7 @@  static int cbs_h2645_read_more_rbsp_data(GetBitContext *gbc)
 #undef xse
 #undef infer
 #undef more_rbsp_data
+#undef bit_position
 #undef byte_alignment
 #undef allocate
 
@@ -424,6 +426,7 @@  static int cbs_h2645_read_more_rbsp_data(GetBitContext *gbc)
 
 #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 { \
@@ -460,6 +463,7 @@  static int cbs_h2645_read_more_rbsp_data(GetBitContext *gbc)
 #undef se
 #undef infer
 #undef more_rbsp_data
+#undef bit_position
 #undef byte_alignment
 #undef allocate
 
@@ -1372,36 +1376,11 @@  static void cbs_h265_close(CodedBitstreamContext *ctx)
         av_buffer_unref(&h265->pps_ref[i]);
 }
 
-static void cbs_h264_free_sei_payload(H264RawSEIPayload *payload)
-{
-    switch (payload->payload_type) {
-    case H264_SEI_TYPE_BUFFERING_PERIOD:
-    case H264_SEI_TYPE_PIC_TIMING:
-    case H264_SEI_TYPE_PAN_SCAN_RECT:
-    case H264_SEI_TYPE_RECOVERY_POINT:
-    case H264_SEI_TYPE_DISPLAY_ORIENTATION:
-    case H264_SEI_TYPE_MASTERING_DISPLAY_COLOUR_VOLUME:
-    case H264_SEI_TYPE_ALTERNATIVE_TRANSFER:
-        break;
-    case H264_SEI_TYPE_USER_DATA_REGISTERED:
-        av_buffer_unref(&payload->payload.user_data_registered.data_ref);
-        break;
-    case H264_SEI_TYPE_USER_DATA_UNREGISTERED:
-        av_buffer_unref(&payload->payload.user_data_unregistered.data_ref);
-        break;
-    default:
-        av_buffer_unref(&payload->payload.other.data_ref);
-        break;
-    }
-}
-
 static void cbs_h264_free_sei(void *opaque, uint8_t *content)
 {
     H264RawSEI *sei = (H264RawSEI*)content;
-    int i;
-    for (i = 0; i < sei->payload_count; i++)
-        cbs_h264_free_sei_payload(&sei->payload[i]);
-    av_freep(&content);
+    ff_cbs_sei_free_message_list(&sei->message_list);
+    av_free(content);
 }
 
 static const CodedBitstreamUnitTypeDescriptor cbs_h264_unit_types[] = {
@@ -1433,42 +1412,11 @@  static const CodedBitstreamUnitTypeDescriptor cbs_h264_unit_types[] = {
     CBS_UNIT_TYPE_END_OF_LIST
 };
 
-static void cbs_h265_free_sei_payload(H265RawSEIPayload *payload)
-{
-    switch (payload->payload_type) {
-    case HEVC_SEI_TYPE_BUFFERING_PERIOD:
-    case HEVC_SEI_TYPE_PICTURE_TIMING:
-    case HEVC_SEI_TYPE_PAN_SCAN_RECT:
-    case HEVC_SEI_TYPE_RECOVERY_POINT:
-    case HEVC_SEI_TYPE_DISPLAY_ORIENTATION:
-    case HEVC_SEI_TYPE_ACTIVE_PARAMETER_SETS:
-    case HEVC_SEI_TYPE_DECODED_PICTURE_HASH:
-    case HEVC_SEI_TYPE_TIME_CODE:
-    case HEVC_SEI_TYPE_MASTERING_DISPLAY_INFO:
-    case HEVC_SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO:
-    case HEVC_SEI_TYPE_ALTERNATIVE_TRANSFER_CHARACTERISTICS:
-    case HEVC_SEI_TYPE_ALPHA_CHANNEL_INFO:
-        break;
-    case HEVC_SEI_TYPE_USER_DATA_REGISTERED_ITU_T_T35:
-        av_buffer_unref(&payload->payload.user_data_registered.data_ref);
-        break;
-    case HEVC_SEI_TYPE_USER_DATA_UNREGISTERED:
-        av_buffer_unref(&payload->payload.user_data_unregistered.data_ref);
-        break;
-    default:
-        av_buffer_unref(&payload->payload.other.data_ref);
-        break;
-    }
-    av_buffer_unref(&payload->extension_data.data_ref);
-}
-
 static void cbs_h265_free_sei(void *opaque, uint8_t *content)
 {
     H265RawSEI *sei = (H265RawSEI*)content;
-    int i;
-    for (i = 0; i < sei->payload_count; i++)
-        cbs_h265_free_sei_payload(&sei->payload[i]);
-    av_freep(&content);
+    ff_cbs_sei_free_message_list(&sei->message_list);
+    av_free(content);
 }
 
 static const CodedBitstreamUnitTypeDescriptor cbs_h265_unit_types[] = {
@@ -1548,92 +1496,164 @@  const CodedBitstreamType ff_cbs_type_h265 = {
     .close             = &cbs_h265_close,
 };
 
-int ff_cbs_h264_add_sei_message(CodedBitstreamFragment *au,
-                                H264RawSEIPayload *payload)
-{
-    H264RawSEI *sei = NULL;
-    int err, i;
-
-    // Find an existing SEI NAL unit to add to.
-    for (i = 0; i < au->nb_units; i++) {
-        if (au->units[i].type == H264_NAL_SEI) {
-            sei = au->units[i].content;
-            if (sei->payload_count < H264_MAX_SEI_PAYLOADS)
-                break;
-
-            sei = NULL;
-        }
-    }
-
-    if (!sei) {
-        // Need to make a new SEI NAL unit.  Insert it before the first
-        // slice data NAL unit; if no slice data, add at the end.
-        AVBufferRef *sei_ref;
-
-        sei = av_mallocz(sizeof(*sei));
-        if (!sei) {
-            err = AVERROR(ENOMEM);
-            goto fail;
-        }
-
-        sei->nal_unit_header.nal_unit_type = H264_NAL_SEI;
-        sei->nal_unit_header.nal_ref_idc   = 0;
-
-        sei_ref = av_buffer_create((uint8_t*)sei, sizeof(*sei),
-                                   &cbs_h264_free_sei, NULL, 0);
-        if (!sei_ref) {
-            av_freep(&sei);
-            err = AVERROR(ENOMEM);
-            goto fail;
-        }
-
-        for (i = 0; i < au->nb_units; i++) {
-            if (au->units[i].type == H264_NAL_SLICE ||
-                au->units[i].type == H264_NAL_IDR_SLICE)
-                break;
-        }
-
-        err = ff_cbs_insert_unit_content(au, i, H264_NAL_SEI,
-                                         sei, sei_ref);
-        av_buffer_unref(&sei_ref);
-        if (err < 0)
-            goto fail;
-    }
+static const SEIMessageTypeDescriptor cbs_sei_common_types[] = {
+    {
+        SEI_TYPE_FILLER_PAYLOAD,
+        1, 1,
+        sizeof(SEIRawFillerPayload),
+        SEI_MESSAGE_RW(sei, filler_payload),
+    },
+    {
+        SEI_TYPE_USER_DATA_REGISTERED_ITU_T_T35,
+        1, 1,
+        sizeof(SEIRawUserDataRegistered),
+        SEI_MESSAGE_RW(sei, user_data_registered),
+    },
+    {
+        SEI_TYPE_USER_DATA_UNREGISTERED,
+        1, 1,
+        sizeof(SEIRawUserDataUnregistered),
+        SEI_MESSAGE_RW(sei, user_data_unregistered),
+    },
+    {
+        SEI_TYPE_MASTERING_DISPLAY_COLOUR_VOLUME,
+        1, 0,
+        sizeof(SEIRawMasteringDisplayColourVolume),
+        SEI_MESSAGE_RW(sei, mastering_display_colour_volume),
+    },
+    {
+        SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO,
+        1, 0,
+        sizeof(SEIRawContentLightLevelInfo),
+        SEI_MESSAGE_RW(sei, content_light_level_info),
+    },
+    {
+        SEI_TYPE_ALTERNATIVE_TRANSFER_CHARACTERISTICS,
+        1, 0,
+        sizeof(SEIRawAlternativeTransferCharacteristics),
+        SEI_MESSAGE_RW(sei, alternative_transfer_characteristics),
+    },
+    SEI_MESSAGE_TYPE_END,
+};
 
-    memcpy(&sei->payload[sei->payload_count], payload, sizeof(*payload));
-    ++sei->payload_count;
+static const SEIMessageTypeDescriptor cbs_sei_h264_types[] = {
+    {
+        SEI_TYPE_BUFFERING_PERIOD,
+        1, 0,
+        sizeof(H264RawSEIBufferingPeriod),
+        SEI_MESSAGE_RW(h264, sei_buffering_period),
+    },
+    {
+        SEI_TYPE_PIC_TIMING,
+        1, 0,
+        sizeof(H264RawSEIPicTiming),
+        SEI_MESSAGE_RW(h264, sei_pic_timing),
+    },
+    {
+        SEI_TYPE_PAN_SCAN_RECT,
+        1, 0,
+        sizeof(H264RawSEIPanScanRect),
+        SEI_MESSAGE_RW(h264, sei_pan_scan_rect),
+    },
+    {
+        SEI_TYPE_RECOVERY_POINT,
+        1, 0,
+        sizeof(H264RawSEIRecoveryPoint),
+        SEI_MESSAGE_RW(h264, sei_recovery_point),
+    },
+    {
+        SEI_TYPE_DISPLAY_ORIENTATION,
+        1, 0,
+        sizeof(H264RawSEIDisplayOrientation),
+        SEI_MESSAGE_RW(h264, sei_display_orientation),
+    },
+    SEI_MESSAGE_TYPE_END
+};
 
-    return 0;
-fail:
-    cbs_h264_free_sei_payload(payload);
-    return err;
-}
+static const SEIMessageTypeDescriptor cbs_sei_h265_types[] = {
+    {
+        SEI_TYPE_BUFFERING_PERIOD,
+        1, 0,
+        sizeof(H265RawSEIBufferingPeriod),
+        SEI_MESSAGE_RW(h265, sei_buffering_period),
+    },
+    {
+        SEI_TYPE_PIC_TIMING,
+        1, 0,
+        sizeof(H265RawSEIPicTiming),
+        SEI_MESSAGE_RW(h265, sei_pic_timing),
+    },
+    {
+        SEI_TYPE_PAN_SCAN_RECT,
+        1, 0,
+        sizeof(H265RawSEIPanScanRect),
+        SEI_MESSAGE_RW(h265, sei_pan_scan_rect),
+    },
+    {
+        SEI_TYPE_RECOVERY_POINT,
+        1, 0,
+        sizeof(H265RawSEIRecoveryPoint),
+        SEI_MESSAGE_RW(h265, sei_recovery_point),
+    },
+    {
+        SEI_TYPE_DISPLAY_ORIENTATION,
+        1, 0,
+        sizeof(H265RawSEIDisplayOrientation),
+        SEI_MESSAGE_RW(h265, sei_display_orientation),
+    },
+    {
+        SEI_TYPE_ACTIVE_PARAMETER_SETS,
+        1, 0,
+        sizeof(H265RawSEIActiveParameterSets),
+        SEI_MESSAGE_RW(h265, sei_active_parameter_sets),
+    },
+    {
+        SEI_TYPE_DECODED_PICTURE_HASH,
+        0, 1,
+        sizeof(H265RawSEIDecodedPictureHash),
+        SEI_MESSAGE_RW(h265, sei_decoded_picture_hash),
+    },
+    {
+        SEI_TYPE_TIME_CODE,
+        1, 0,
+        sizeof(H265RawSEITimeCode),
+        SEI_MESSAGE_RW(h265, sei_time_code),
+    },
+    {
+        SEI_TYPE_ALPHA_CHANNEL_INFO,
+        1, 0,
+        sizeof(H265RawSEIAlphaChannelInfo),
+        SEI_MESSAGE_RW(h265, sei_alpha_channel_info),
+    },
+    SEI_MESSAGE_TYPE_END
+};
 
-void ff_cbs_h264_delete_sei_message(CodedBitstreamFragment *au,
-                                    CodedBitstreamUnit *nal,
-                                    int position)
+const SEIMessageTypeDescriptor *ff_cbs_sei_find_type(CodedBitstreamContext *ctx,
+                                                     int payload_type)
 {
-    H264RawSEI *sei = nal->content;
-
-    av_assert0(nal->type == H264_NAL_SEI);
-    av_assert0(position >= 0 && position < sei->payload_count);
-
-    if (position == 0 && sei->payload_count == 1) {
-        // Deleting NAL unit entirely.
-        int i;
+    const SEIMessageTypeDescriptor *codec_list;
+    int i;
 
-        for (i = 0; i < au->nb_units; i++) {
-            if (&au->units[i] == nal)
-                break;
-        }
+    for (i = 0; cbs_sei_common_types[i].type >= 0; i++) {
+        if (cbs_sei_common_types[i].type == payload_type)
+            return &cbs_sei_common_types[i];
+    }
 
-        ff_cbs_delete_unit(au, i);
-    } else {
-        cbs_h264_free_sei_payload(&sei->payload[position]);
+    switch (ctx->codec->codec_id) {
+    case AV_CODEC_ID_H264:
+        codec_list = cbs_sei_h264_types;
+        break;
+    case AV_CODEC_ID_H265:
+        codec_list = cbs_sei_h265_types;
+        break;
+    default:
+        return NULL;
+    }
 
-        --sei->payload_count;
-        memmove(sei->payload + position,
-                sei->payload + position + 1,
-                (sei->payload_count - position) * sizeof(*sei->payload));
+    for (i = 0; codec_list[i].type >= 0; i++) {
+        if (codec_list[i].type == payload_type)
+            return &codec_list[i];
     }
+
+    return NULL;
 }
diff --git a/libavcodec/cbs_h264_syntax_template.c b/libavcodec/cbs_h264_syntax_template.c
index 76ed51cc7b..9587f33985 100644
--- a/libavcodec/cbs_h264_syntax_template.c
+++ b/libavcodec/cbs_h264_syntax_template.c
@@ -511,7 +511,8 @@  static int FUNC(pps)(CodedBitstreamContext *ctx, RWContext *rw,
 }
 
 static int FUNC(sei_buffering_period)(CodedBitstreamContext *ctx, RWContext *rw,
-                                      H264RawSEIBufferingPeriod *current)
+                                      H264RawSEIBufferingPeriod *current,
+                                      SEIMessageState *sei)
 {
     CodedBitstreamH264Context *h264 = ctx->priv_data;
     const H264RawSPS *sps;
@@ -604,7 +605,8 @@  static int FUNC(sei_pic_timestamp)(CodedBitstreamContext *ctx, RWContext *rw,
 }
 
 static int FUNC(sei_pic_timing)(CodedBitstreamContext *ctx, RWContext *rw,
-                                H264RawSEIPicTiming *current)
+                                H264RawSEIPicTiming *current,
+                                SEIMessageState *sei)
 {
     CodedBitstreamH264Context *h264 = ctx->priv_data;
     const H264RawSPS *sps;
@@ -675,7 +677,8 @@  static int FUNC(sei_pic_timing)(CodedBitstreamContext *ctx, RWContext *rw,
 }
 
 static int FUNC(sei_pan_scan_rect)(CodedBitstreamContext *ctx, RWContext *rw,
-                                   H264RawSEIPanScanRect *current)
+                                   H264RawSEIPanScanRect *current,
+                                   SEIMessageState *sei)
 {
     int err, i;
 
@@ -701,7 +704,8 @@  static int FUNC(sei_pan_scan_rect)(CodedBitstreamContext *ctx, RWContext *rw,
 }
 
 static int FUNC(sei_recovery_point)(CodedBitstreamContext *ctx, RWContext *rw,
-                                    H264RawSEIRecoveryPoint *current)
+                                    H264RawSEIRecoveryPoint *current,
+                                    SEIMessageState *sei)
 {
     int err;
 
@@ -716,7 +720,8 @@  static int FUNC(sei_recovery_point)(CodedBitstreamContext *ctx, RWContext *rw,
 }
 
 static int FUNC(sei_display_orientation)(CodedBitstreamContext *ctx, RWContext *rw,
-                                         H264RawSEIDisplayOrientation *current)
+                                         H264RawSEIDisplayOrientation *current,
+                                         SEIMessageState *sei)
 {
     int err;
 
@@ -734,171 +739,17 @@  static int FUNC(sei_display_orientation)(CodedBitstreamContext *ctx, RWContext *
     return 0;
 }
 
-static int FUNC(sei_payload)(CodedBitstreamContext *ctx, RWContext *rw,
-                             H264RawSEIPayload *current)
-{
-    int err, i;
-    int start_position, end_position;
-
-#ifdef READ
-    start_position = get_bits_count(rw);
-#else
-    start_position = put_bits_count(rw);
-#endif
-
-    switch (current->payload_type) {
-    case H264_SEI_TYPE_BUFFERING_PERIOD:
-        CHECK(FUNC(sei_buffering_period)
-              (ctx, rw, &current->payload.buffering_period));
-        break;
-    case H264_SEI_TYPE_PIC_TIMING:
-        CHECK(FUNC(sei_pic_timing)
-              (ctx, rw, &current->payload.pic_timing));
-        break;
-    case H264_SEI_TYPE_PAN_SCAN_RECT:
-        CHECK(FUNC(sei_pan_scan_rect)
-              (ctx, rw, &current->payload.pan_scan_rect));
-        break;
-    case H264_SEI_TYPE_FILLER_PAYLOAD:
-        {
-            for (i = 0; i  < current->payload_size; i++)
-                fixed(8, ff_byte, 0xff);
-        }
-        break;
-    case H264_SEI_TYPE_USER_DATA_REGISTERED:
-        CHECK(FUNC_SEI(sei_user_data_registered)
-              (ctx, rw, &current->payload.user_data_registered, &current->payload_size));
-        break;
-    case H264_SEI_TYPE_USER_DATA_UNREGISTERED:
-        CHECK(FUNC_SEI(sei_user_data_unregistered)
-              (ctx, rw, &current->payload.user_data_unregistered, &current->payload_size));
-        break;
-    case H264_SEI_TYPE_RECOVERY_POINT:
-        CHECK(FUNC(sei_recovery_point)
-              (ctx, rw, &current->payload.recovery_point));
-        break;
-    case H264_SEI_TYPE_DISPLAY_ORIENTATION:
-        CHECK(FUNC(sei_display_orientation)
-              (ctx, rw, &current->payload.display_orientation));
-        break;
-    case H264_SEI_TYPE_MASTERING_DISPLAY_COLOUR_VOLUME:
-        CHECK(FUNC_SEI(sei_mastering_display_colour_volume)
-              (ctx, rw, &current->payload.mastering_display_colour_volume));
-        break;
-    case H264_SEI_TYPE_ALTERNATIVE_TRANSFER:
-        CHECK(FUNC_SEI(sei_alternative_transfer_characteristics)
-              (ctx, rw, &current->payload.alternative_transfer_characteristics));
-        break;
-    default:
-        {
-#ifdef READ
-            current->payload.other.data_length = current->payload_size;
-#endif
-            allocate(current->payload.other.data, current->payload.other.data_length);
-            for (i = 0; i < current->payload.other.data_length; i++)
-                xu(8, payload_byte[i], current->payload.other.data[i], 0, 255, 1, i);
-        }
-    }
-
-    if (byte_alignment(rw)) {
-        fixed(1, bit_equal_to_one, 1);
-        while (byte_alignment(rw))
-            fixed(1, bit_equal_to_zero, 0);
-    }
-
-#ifdef READ
-    end_position = get_bits_count(rw);
-    if (end_position < start_position + 8 * current->payload_size) {
-        av_log(ctx->log_ctx, AV_LOG_ERROR, "Incorrect SEI payload length: "
-               "header %"PRIu32" bits, actually %d bits.\n",
-               8 * current->payload_size,
-               end_position - start_position);
-        return AVERROR_INVALIDDATA;
-    }
-#else
-    end_position = put_bits_count(rw);
-    current->payload_size = (end_position - start_position) / 8;
-#endif
-
-    return 0;
-}
-
 static int FUNC(sei)(CodedBitstreamContext *ctx, RWContext *rw,
                      H264RawSEI *current)
 {
-    int err, k;
+    int err;
 
     HEADER("Supplemental Enhancement Information");
 
     CHECK(FUNC(nal_unit_header)(ctx, rw, &current->nal_unit_header,
                                 1 << H264_NAL_SEI));
 
-#ifdef READ
-    for (k = 0; k < H264_MAX_SEI_PAYLOADS; k++) {
-        uint32_t payload_type = 0;
-        uint32_t payload_size = 0;
-        uint32_t tmp;
-
-        while (show_bits(rw, 8) == 0xff) {
-            fixed(8, ff_byte, 0xff);
-            payload_type += 255;
-        }
-        xu(8, last_payload_type_byte, tmp, 0, 254, 0);
-        payload_type += tmp;
-
-        while (show_bits(rw, 8) == 0xff) {
-            fixed(8, ff_byte, 0xff);
-            payload_size += 255;
-        }
-        xu(8, last_payload_size_byte, tmp, 0, 254, 0);
-        payload_size += tmp;
-
-        current->payload[k].payload_type = payload_type;
-        current->payload[k].payload_size = payload_size;
-
-        current->payload_count++;
-        CHECK(FUNC(sei_payload)(ctx, rw, &current->payload[k]));
-
-        if (!cbs_h2645_read_more_rbsp_data(rw))
-            break;
-    }
-    if (k >= H264_MAX_SEI_PAYLOADS) {
-        av_log(ctx->log_ctx, AV_LOG_ERROR, "Too many payloads in "
-               "SEI message: found %d.\n", k);
-        return AVERROR_INVALIDDATA;
-    }
-#else
-    for (k = 0; k < current->payload_count; k++) {
-        PutBitContext start_state;
-        uint32_t tmp;
-        int need_size, i;
-
-        // Somewhat clumsy: we write the payload twice when
-        // we don't know the size in advance.  This will mess
-        // with trace output, but is otherwise harmless.
-        start_state = *rw;
-        need_size = !current->payload[k].payload_size;
-        for (i = 0; i < 1 + need_size; i++) {
-            *rw = start_state;
-
-            tmp = current->payload[k].payload_type;
-            while (tmp >= 255) {
-                fixed(8, ff_byte, 0xff);
-                tmp -= 255;
-            }
-            xu(8, last_payload_type_byte, tmp, 0, 254, 0);
-
-            tmp = current->payload[k].payload_size;
-            while (tmp >= 255) {
-                fixed(8, ff_byte, 0xff);
-                tmp -= 255;
-            }
-            xu(8, last_payload_size_byte, tmp, 0, 254, 0);
-
-            CHECK(FUNC(sei_payload)(ctx, rw, &current->payload[k]));
-        }
-    }
-#endif
+    CHECK(FUNC_SEI(message_list)(ctx, rw, &current->message_list, 1));
 
     CHECK(FUNC(rbsp_trailing_bits)(ctx, rw));
 
diff --git a/libavcodec/cbs_h265.h b/libavcodec/cbs_h265.h
index d8e93e3bb8..738cbeec2c 100644
--- a/libavcodec/cbs_h265.h
+++ b/libavcodec/cbs_h265.h
@@ -658,40 +658,9 @@  typedef struct H265RawSEIAlphaChannelInfo {
     uint8_t  alpha_channel_clip_type_flag;
 } H265RawSEIAlphaChannelInfo;
 
-typedef struct H265RawSEIPayload {
-    uint32_t payload_type;
-    uint32_t payload_size;
-    union {
-        H265RawSEIBufferingPeriod buffering_period;
-        H265RawSEIPicTiming pic_timing;
-        H265RawSEIPanScanRect pan_scan_rect;
-        SEIRawUserDataRegistered user_data_registered;
-        SEIRawUserDataUnregistered user_data_unregistered;
-        H265RawSEIRecoveryPoint recovery_point;
-        H265RawSEIDisplayOrientation display_orientation;
-        H265RawSEIActiveParameterSets active_parameter_sets;
-        H265RawSEIDecodedPictureHash decoded_picture_hash;
-        H265RawSEITimeCode time_code;
-        SEIRawMasteringDisplayColourVolume
-            mastering_display_colour_volume;
-        SEIRawContentLightLevelInfo content_light_level;
-        SEIRawAlternativeTransferCharacteristics
-            alternative_transfer_characteristics;
-        H265RawSEIAlphaChannelInfo alpha_channel_info;
-        struct {
-            uint8_t     *data;
-            AVBufferRef *data_ref;
-            size_t       data_length;
-        } other;
-    } payload;
-    H265RawExtensionData extension_data;
-} H265RawSEIPayload;
-
 typedef struct H265RawSEI {
     H265RawNALUnitHeader nal_unit_header;
-
-    H265RawSEIPayload payload[H265_MAX_SEI_PAYLOADS];
-    uint8_t payload_count;
+    SEIRawMessageList    message_list;
 } H265RawSEI;
 
 typedef struct CodedBitstreamH265Context {
diff --git a/libavcodec/cbs_h265_syntax_template.c b/libavcodec/cbs_h265_syntax_template.c
index e4cc1a9be8..d09934cfeb 100644
--- a/libavcodec/cbs_h265_syntax_template.c
+++ b/libavcodec/cbs_h265_syntax_template.c
@@ -1596,10 +1596,9 @@  static int FUNC(slice_segment_header)(CodedBitstreamContext *ctx, RWContext *rw,
     return 0;
 }
 
-static int FUNC(sei_buffering_period)(CodedBitstreamContext *ctx, RWContext *rw,
-                                      H265RawSEIBufferingPeriod *current,
-                                      uint32_t *payload_size,
-                                      int *more_data)
+static int FUNC(sei_buffering_period)
+    (CodedBitstreamContext *ctx, RWContext *rw,
+     H265RawSEIBufferingPeriod *current, SEIMessageState *sei)
 {
     CodedBitstreamH265Context *h265 = ctx->priv_data;
     const H265RawSPS *sps;
@@ -1687,7 +1686,7 @@  static int FUNC(sei_buffering_period)(CodedBitstreamContext *ctx, RWContext *rw,
 
 #ifdef READ
     end_pos = get_bits_count(rw);
-    if (cbs_h265_payload_extension_present(rw, *payload_size,
+    if (cbs_h265_payload_extension_present(rw, sei->payload_size,
                                            end_pos - start_pos))
         flag(use_alt_cpb_params_flag);
     else
@@ -1695,20 +1694,21 @@  static int FUNC(sei_buffering_period)(CodedBitstreamContext *ctx, RWContext *rw,
 #else
     // If unknown extension data exists, then use_alt_cpb_params_flag is
     // coded in the bitstream and must be written even if it's 0.
-    if (current->use_alt_cpb_params_flag || *more_data) {
+    if (current->use_alt_cpb_params_flag || sei->extension_present) {
         flag(use_alt_cpb_params_flag);
         // Ensure this bit is not the last in the payload by making the
         // more_data_in_payload() check evaluate to true, so it may not
         // be mistaken as something else by decoders.
-        *more_data = 1;
+        sei->extension_present = 1;
     }
 #endif
 
     return 0;
 }
 
-static int FUNC(sei_pic_timing)(CodedBitstreamContext *ctx, RWContext *rw,
-                                H265RawSEIPicTiming *current)
+static int FUNC(sei_pic_timing)
+    (CodedBitstreamContext *ctx, RWContext *rw,
+     H265RawSEIPicTiming *current, SEIMessageState *sei)
 {
     CodedBitstreamH265Context *h265 = ctx->priv_data;
     const H265RawSPS *sps;
@@ -1782,8 +1782,9 @@  static int FUNC(sei_pic_timing)(CodedBitstreamContext *ctx, RWContext *rw,
     return 0;
 }
 
-static int FUNC(sei_pan_scan_rect)(CodedBitstreamContext *ctx, RWContext *rw,
-                                   H265RawSEIPanScanRect *current)
+static int FUNC(sei_pan_scan_rect)
+    (CodedBitstreamContext *ctx, RWContext *rw,
+     H265RawSEIPanScanRect *current, SEIMessageState *sei)
 {
     int err, i;
 
@@ -1808,8 +1809,9 @@  static int FUNC(sei_pan_scan_rect)(CodedBitstreamContext *ctx, RWContext *rw,
     return 0;
 }
 
-static int FUNC(sei_recovery_point)(CodedBitstreamContext *ctx, RWContext *rw,
-                                    H265RawSEIRecoveryPoint *current)
+static int FUNC(sei_recovery_point)
+    (CodedBitstreamContext *ctx, RWContext *rw,
+     H265RawSEIRecoveryPoint *current, SEIMessageState *sei)
 {
     int err;
 
@@ -1823,8 +1825,9 @@  static int FUNC(sei_recovery_point)(CodedBitstreamContext *ctx, RWContext *rw,
     return 0;
 }
 
-static int FUNC(sei_display_orientation)(CodedBitstreamContext *ctx, RWContext *rw,
-                                         H265RawSEIDisplayOrientation *current)
+static int FUNC(sei_display_orientation)
+    (CodedBitstreamContext *ctx, RWContext *rw,
+     H265RawSEIDisplayOrientation *current, SEIMessageState *sei)
 {
     int err;
 
@@ -1841,8 +1844,9 @@  static int FUNC(sei_display_orientation)(CodedBitstreamContext *ctx, RWContext *
     return 0;
 }
 
-static int FUNC(sei_active_parameter_sets)(CodedBitstreamContext *ctx, RWContext *rw,
-                                           H265RawSEIActiveParameterSets *current)
+static int FUNC(sei_active_parameter_sets)
+    (CodedBitstreamContext *ctx, RWContext *rw,
+     H265RawSEIActiveParameterSets *current, SEIMessageState *sei)
 {
     CodedBitstreamH265Context *h265 = ctx->priv_data;
     const H265RawVPS *vps;
@@ -1877,8 +1881,9 @@  static int FUNC(sei_active_parameter_sets)(CodedBitstreamContext *ctx, RWContext
     return 0;
 }
 
-static int FUNC(sei_decoded_picture_hash)(CodedBitstreamContext *ctx, RWContext *rw,
-                                          H265RawSEIDecodedPictureHash *current)
+static int FUNC(sei_decoded_picture_hash)
+    (CodedBitstreamContext *ctx, RWContext *rw,
+     H265RawSEIDecodedPictureHash *current, SEIMessageState *sei)
 {
     CodedBitstreamH265Context *h265 = ctx->priv_data;
     const H265RawSPS *sps = h265->active_sps;
@@ -1908,8 +1913,9 @@  static int FUNC(sei_decoded_picture_hash)(CodedBitstreamContext *ctx, RWContext
     return 0;
 }
 
-static int FUNC(sei_time_code)(CodedBitstreamContext *ctx, RWContext *rw,
-                               H265RawSEITimeCode *current)
+static int FUNC(sei_time_code)
+    (CodedBitstreamContext *ctx, RWContext *rw,
+     H265RawSEITimeCode *current, SEIMessageState *sei)
 {
     int err, i;
 
@@ -1958,9 +1964,9 @@  static int FUNC(sei_time_code)(CodedBitstreamContext *ctx, RWContext *rw,
     return 0;
 }
 
-static int FUNC(sei_alpha_channel_info)(CodedBitstreamContext *ctx,
-                                        RWContext *rw,
-                                        H265RawSEIAlphaChannelInfo *current)
+static int FUNC(sei_alpha_channel_info)
+    (CodedBitstreamContext *ctx, RWContext *rw,
+     H265RawSEIAlphaChannelInfo *current, SEIMessageState *sei)
 {
     int err, length;
 
@@ -1986,156 +1992,10 @@  static int FUNC(sei_alpha_channel_info)(CodedBitstreamContext *ctx,
     return 0;
 }
 
-static int FUNC(payload_extension)(CodedBitstreamContext *ctx, RWContext *rw,
-                                   H265RawExtensionData *current, uint32_t payload_size,
-                                   int cur_pos)
-{
-    int err;
-    size_t byte_length, k;
-
-#ifdef READ
-    GetBitContext tmp;
-    int bits_left, payload_zero_bits;
-
-    if (!cbs_h265_payload_extension_present(rw, payload_size, cur_pos))
-        return 0;
-
-    bits_left = 8 * payload_size - cur_pos;
-    tmp = *rw;
-    if (bits_left > 8)
-        skip_bits_long(&tmp, bits_left - 8);
-    payload_zero_bits = get_bits(&tmp, FFMIN(bits_left, 8));
-    if (!payload_zero_bits)
-        return AVERROR_INVALIDDATA;
-    payload_zero_bits = ff_ctz(payload_zero_bits);
-    current->bit_length = bits_left - payload_zero_bits - 1;
-    allocate(current->data, (current->bit_length + 7) / 8);
-#endif
-
-    byte_length = (current->bit_length + 7) / 8;
-    for (k = 0; k < byte_length; k++) {
-        int length = FFMIN(current->bit_length - k * 8, 8);
-        xu(length, reserved_payload_extension_data, current->data[k],
-           0, MAX_UINT_BITS(length), 0);
-    }
-
-    return 0;
-}
-
-static int FUNC(sei_payload)(CodedBitstreamContext *ctx, RWContext *rw,
-                             H265RawSEIPayload *current, int prefix)
-{
-    int err, i;
-    int start_position, current_position;
-    int more_data = !!current->extension_data.bit_length;
-
-#ifdef READ
-    start_position = get_bits_count(rw);
-#else
-    start_position = put_bits_count(rw);
-#endif
-
-    switch (current->payload_type) {
-#define SEI_TYPE_CHECK_VALID(name, prefix_valid, suffix_valid) do { \
-            if (prefix && !prefix_valid) { \
-                av_log(ctx->log_ctx, AV_LOG_ERROR, "SEI type %s invalid " \
-                       "as prefix SEI!\n", #name); \
-                return AVERROR_INVALIDDATA; \
-            } \
-            if (!prefix && !suffix_valid) { \
-                av_log(ctx->log_ctx, AV_LOG_ERROR, "SEI type %s invalid " \
-                       "as suffix SEI!\n", #name); \
-                return AVERROR_INVALIDDATA; \
-            } \
-        } while (0)
-#define SEI_TYPE_N(type, prefix_valid, suffix_valid, name) \
-    case HEVC_SEI_TYPE_ ## type: \
-        SEI_TYPE_CHECK_VALID(name, prefix_valid, suffix_valid); \
-        CHECK(FUNC(sei_ ## name)(ctx, rw, &current->payload.name)); \
-        break
-#define SEI_TYPE_S(type, prefix_valid, suffix_valid, name) \
-    case HEVC_SEI_TYPE_ ## type: \
-        SEI_TYPE_CHECK_VALID(name, prefix_valid, suffix_valid); \
-        CHECK(FUNC(sei_ ## name)(ctx, rw, &current->payload.name, \
-                                 &current->payload_size)); \
-        break
-#define SEI_TYPE_E(type, prefix_valid, suffix_valid, name) \
-    case HEVC_SEI_TYPE_ ## type: \
-        SEI_TYPE_CHECK_VALID(name, prefix_valid, suffix_valid); \
-        CHECK(FUNC(sei_ ## name)(ctx, rw, &current->payload.name, \
-                                 &current->payload_size, \
-                                 &more_data)); \
-        break
-
-#define SEI_TYPE_N2(type, prefix_valid, suffix_valid, name) \
-    case HEVC_SEI_TYPE_ ## type: \
-        SEI_TYPE_CHECK_VALID(name, prefix_valid, suffix_valid); \
-        CHECK(FUNC_SEI(sei_ ## name)(ctx, rw, &current->payload.name)); \
-        break
-#define SEI_TYPE_S2(type, prefix_valid, suffix_valid, name) \
-    case HEVC_SEI_TYPE_ ## type: \
-        SEI_TYPE_CHECK_VALID(name, prefix_valid, suffix_valid); \
-        CHECK(FUNC_SEI(sei_ ## name)(ctx, rw, &current->payload.name, \
-                                     &current->payload_size)); \
-        break
-
-        SEI_TYPE_E(BUFFERING_PERIOD,         1, 0, buffering_period);
-        SEI_TYPE_N(PICTURE_TIMING,           1, 0, pic_timing);
-        SEI_TYPE_N(PAN_SCAN_RECT,            1, 0, pan_scan_rect);
-        SEI_TYPE_S2(USER_DATA_REGISTERED_ITU_T_T35,
-                                             1, 1, user_data_registered);
-        SEI_TYPE_S2(USER_DATA_UNREGISTERED,  1, 1, user_data_unregistered);
-        SEI_TYPE_N(RECOVERY_POINT,           1, 0, recovery_point);
-        SEI_TYPE_N(DISPLAY_ORIENTATION,      1, 0, display_orientation);
-        SEI_TYPE_N(ACTIVE_PARAMETER_SETS,    1, 0, active_parameter_sets);
-        SEI_TYPE_N(DECODED_PICTURE_HASH,     0, 1, decoded_picture_hash);
-        SEI_TYPE_N(TIME_CODE,                1, 0, time_code);
-        SEI_TYPE_N2(MASTERING_DISPLAY_INFO,  1, 0, mastering_display_colour_volume);
-        SEI_TYPE_N2(CONTENT_LIGHT_LEVEL_INFO,1, 0, content_light_level);
-        SEI_TYPE_N2(ALTERNATIVE_TRANSFER_CHARACTERISTICS,
-                                             1, 0, alternative_transfer_characteristics);
-        SEI_TYPE_N(ALPHA_CHANNEL_INFO,       1, 0, alpha_channel_info);
-
-#undef SEI_TYPE
-    default:
-        {
-#ifdef READ
-            current->payload.other.data_length = current->payload_size;
-#endif
-            allocate(current->payload.other.data, current->payload.other.data_length);
-
-            for (i = 0; i < current->payload_size; i++)
-                xu(8, payload_byte[i], current->payload.other.data[i], 0, 255,
-                   1, i);
-        }
-    }
-
-    // more_data_in_payload()
-#ifdef READ
-    current_position = get_bits_count(rw) - start_position;
-    if (current_position < 8 * current->payload_size) {
-#else
-    current_position = put_bits_count(rw) - start_position;
-    if (byte_alignment(rw) || more_data) {
-#endif
-        CHECK(FUNC(payload_extension)(ctx, rw, &current->extension_data,
-                                      current->payload_size, current_position));
-        fixed(1, bit_equal_to_one, 1);
-        while (byte_alignment(rw))
-            fixed(1, bit_equal_to_zero, 0);
-    }
-
-#ifdef WRITE
-    current->payload_size = (put_bits_count(rw) - start_position) >> 3;
-#endif
-
-    return 0;
-}
-
 static int FUNC(sei)(CodedBitstreamContext *ctx, RWContext *rw,
                      H265RawSEI *current, int prefix)
 {
-    int err, k;
+    int err;
 
     if (prefix)
         HEADER("Prefix Supplemental Enhancement Information");
@@ -2146,72 +2006,7 @@  static int FUNC(sei)(CodedBitstreamContext *ctx, RWContext *rw,
                                 prefix ? HEVC_NAL_SEI_PREFIX
                                        : HEVC_NAL_SEI_SUFFIX));
 
-#ifdef READ
-    for (k = 0; k < H265_MAX_SEI_PAYLOADS; k++) {
-        uint32_t payload_type = 0;
-        uint32_t payload_size = 0;
-        uint32_t tmp;
-
-        while (show_bits(rw, 8) == 0xff) {
-            fixed(8, ff_byte, 0xff);
-            payload_type += 255;
-        }
-        xu(8, last_payload_type_byte, tmp, 0, 254, 0);
-        payload_type += tmp;
-
-        while (show_bits(rw, 8) == 0xff) {
-            fixed(8, ff_byte, 0xff);
-            payload_size += 255;
-        }
-        xu(8, last_payload_size_byte, tmp, 0, 254, 0);
-        payload_size += tmp;
-
-        current->payload[k].payload_type = payload_type;
-        current->payload[k].payload_size = payload_size;
-
-        current->payload_count++;
-        CHECK(FUNC(sei_payload)(ctx, rw, &current->payload[k], prefix));
-
-        if (!cbs_h2645_read_more_rbsp_data(rw))
-            break;
-    }
-    if (k >= H265_MAX_SEI_PAYLOADS) {
-        av_log(ctx->log_ctx, AV_LOG_ERROR, "Too many payloads in "
-               "SEI message: found %d.\n", k);
-        return AVERROR_INVALIDDATA;
-    }
-#else
-    for (k = 0; k < current->payload_count; k++) {
-        PutBitContext start_state;
-        uint32_t tmp;
-        int need_size, i;
-
-        // Somewhat clumsy: we write the payload twice when
-        // we don't know the size in advance.  This will mess
-        // with trace output, but is otherwise harmless.
-        start_state = *rw;
-        need_size = !current->payload[k].payload_size;
-        for (i = 0; i < 1 + need_size; i++) {
-            *rw = start_state;
-
-            tmp = current->payload[k].payload_type;
-            while (tmp >= 255) {
-                fixed(8, ff_byte, 0xff);
-                tmp -= 255;
-            }
-            xu(8, last_payload_type_byte, tmp, 0, 254, 0);
-
-            tmp = current->payload[k].payload_size;
-            while (tmp >= 255) {
-                fixed(8, ff_byte, 0xff);
-                tmp -= 255;
-            }
-            xu(8, last_payload_size_byte, tmp, 0, 254, 0);
-
-            CHECK(FUNC(sei_payload)(ctx, rw, &current->payload[k], prefix));
-        }
-    }
-#endif
+    CHECK(FUNC_SEI(message_list)(ctx, rw, &current->message_list, prefix));
 
     CHECK(FUNC(rbsp_trailing_bits)(ctx, rw));
 
diff --git a/libavcodec/cbs_sei.c b/libavcodec/cbs_sei.c
new file mode 100644
index 0000000000..323997b600
--- /dev/null
+++ b/libavcodec/cbs_sei.c
@@ -0,0 +1,369 @@ 
+/*
+ * 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 "cbs.h"
+#include "cbs_internal.h"
+#include "cbs_h264.h"
+#include "cbs_h265.h"
+#include "cbs_sei.h"
+
+static void cbs_free_user_data_registered(void *opaque, uint8_t *data)
+{
+    SEIRawUserDataRegistered *udr = (SEIRawUserDataRegistered*)data;
+    av_buffer_unref(&udr->data_ref);
+    av_free(udr);
+}
+
+static void cbs_free_user_data_unregistered(void *opaque, uint8_t *data)
+{
+    SEIRawUserDataUnregistered *udu = (SEIRawUserDataUnregistered*)data;
+    av_buffer_unref(&udu->data_ref);
+    av_free(udu);
+}
+
+int ff_cbs_sei_alloc_message_payload(SEIRawMessage *message,
+                                     const SEIMessageTypeDescriptor *desc)
+{
+    void (*free_func)(void*, uint8_t*);
+
+    av_assert0(message->payload     == NULL &&
+               message->payload_ref == NULL);
+    message->payload_type = desc->type;
+
+    if (desc->type == SEI_TYPE_USER_DATA_REGISTERED_ITU_T_T35)
+        free_func = &cbs_free_user_data_registered;
+    else if (desc->type == SEI_TYPE_USER_DATA_UNREGISTERED)
+        free_func = &cbs_free_user_data_unregistered;
+    else
+        free_func = NULL;
+
+    if (free_func) {
+        message->payload = av_mallocz(desc->size);
+        if (!message->payload)
+            return AVERROR(ENOMEM);
+        message->payload_ref =
+            av_buffer_create(message->payload, desc->size,
+                             free_func, NULL, 0);
+    } else {
+        message->payload_ref = av_buffer_alloc(desc->size);
+    }
+    if (!message->payload_ref)
+        return AVERROR(ENOMEM);
+    message->payload = message->payload_ref->data;
+
+    return 0;
+}
+
+int ff_cbs_sei_list_add(SEIRawMessageList *list)
+{
+    void *ptr;
+    int old_count = list->nb_messages_allocated;
+
+    av_assert0(list->nb_messages <= old_count);
+    if (list->nb_messages + 1 > old_count) {
+        int new_count = 2 * old_count + 1;
+
+        ptr = av_realloc_array(list->messages,
+                               new_count, sizeof(*list->messages));
+        if (!ptr)
+            return AVERROR(ENOMEM);
+
+        list->messages = ptr;
+        list->nb_messages_allocated = new_count;
+
+        // Zero the newly-added entries.
+        memset(list->messages + old_count, 0,
+               (new_count - old_count) * sizeof(*list->messages));
+    }
+    ++list->nb_messages;
+    return 0;
+}
+
+void ff_cbs_sei_free_message_list(SEIRawMessageList *list)
+{
+    for (int i = 0; i < list->nb_messages; i++) {
+        SEIRawMessage *message = &list->messages[i];
+        av_buffer_unref(&message->payload_ref);
+        av_buffer_unref(&message->extension_data_ref);
+    }
+    av_free(list->messages);
+}
+
+static int cbs_sei_get_unit(CodedBitstreamContext *ctx,
+                            CodedBitstreamFragment *au,
+                            int prefix,
+                            CodedBitstreamUnit **sei_unit)
+{
+    CodedBitstreamUnit *unit;
+    int sei_type, highest_vcl_type, err, i, position;
+
+    switch (ctx->codec->codec_id) {
+    case AV_CODEC_ID_H264:
+        // (We can ignore auxiliary slices because we only have prefix
+        // SEI in H.264 and an auxiliary picture must always follow a
+        // primary picture.)
+        highest_vcl_type = H264_NAL_IDR_SLICE;
+        if (prefix)
+            sei_type = H264_NAL_SEI;
+        else
+            return AVERROR(EINVAL);
+        break;
+    case AV_CODEC_ID_H265:
+        highest_vcl_type = HEVC_NAL_RSV_VCL31;
+        if (prefix)
+            sei_type = HEVC_NAL_SEI_PREFIX;
+        else
+            sei_type = HEVC_NAL_SEI_SUFFIX;
+        break;
+    default:
+        return AVERROR(EINVAL);
+    }
+
+    // Find an existing SEI NAL unit of the right type.
+    unit = NULL;
+    for (i = 0; i < au->nb_units; i++) {
+        if (au->units[i].type == sei_type) {
+            unit = &au->units[i];
+            break;
+        }
+    }
+
+    if (unit) {
+        *sei_unit = unit;
+        return 0;
+    }
+
+    // Need to add a new SEI NAL unit ...
+    if (prefix) {
+        // ... before the first VCL NAL unit.
+        for (i = 0; i < au->nb_units; i++) {
+            if (au->units[i].type < highest_vcl_type)
+                break;
+        }
+        position = i;
+    } else {
+        // ... after the last VCL NAL unit.
+        for (i = au->nb_units; i >= 0; i--) {
+            if (au->units[i].type < highest_vcl_type)
+                break;
+        }
+        if (i < 0) {
+            // No VCL units; just put it at the end.
+            position = -1;
+        } else {
+            position = i + 1;
+        }
+    }
+
+    err = ff_cbs_insert_unit_content(au, position, sei_type,
+                                     NULL, NULL);
+    if (err < 0)
+        return err;
+    unit = &au->units[position];
+    unit->type = sei_type;
+
+    err = ff_cbs_alloc_unit_content2(ctx, unit);
+    if (err < 0)
+        return err;
+
+    switch (ctx->codec->codec_id) {
+    case AV_CODEC_ID_H264:
+        {
+            H264RawSEI sei = {
+                .nal_unit_header = {
+                    .nal_ref_idc   = 0,
+                    .nal_unit_type = sei_type,
+                },
+            };
+            memcpy(unit->content, &sei, sizeof(sei));
+        }
+        break;
+    case AV_CODEC_ID_H265:
+        {
+            H265RawSEI 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);
+    }
+
+    *sei_unit = unit;
+    return 0;
+}
+
+static int cbs_sei_get_message_list(CodedBitstreamContext *ctx,
+                                    CodedBitstreamUnit *unit,
+                                    SEIRawMessageList **list)
+{
+    switch (ctx->codec->codec_id) {
+    case AV_CODEC_ID_H264:
+        {
+            H264RawSEI *sei = unit->content;
+            if (unit->type != H264_NAL_SEI)
+                return AVERROR(EINVAL);
+            *list = &sei->message_list;
+        }
+        break;
+    case AV_CODEC_ID_H265:
+        {
+            H265RawSEI *sei = unit->content;
+            if (unit->type != HEVC_NAL_SEI_PREFIX &&
+                unit->type != HEVC_NAL_SEI_SUFFIX)
+                return AVERROR(EINVAL);
+            *list = &sei->message_list;
+        }
+        break;
+    default:
+        return AVERROR(EINVAL);
+    }
+
+    return 0;
+}
+
+int ff_cbs_sei_add_message(CodedBitstreamContext *ctx,
+                           CodedBitstreamFragment *au,
+                           int prefix,
+                           uint32_t     payload_type,
+                           void        *payload_data,
+                           AVBufferRef *payload_buf)
+{
+    const SEIMessageTypeDescriptor *desc;
+    CodedBitstreamUnit *unit;
+    SEIRawMessageList *list;
+    SEIRawMessage *message;
+    AVBufferRef *payload_ref;
+    int err;
+
+    desc = ff_cbs_sei_find_type(ctx, payload_type);
+    if (!desc)
+        return AVERROR(EINVAL);
+
+    if (payload_buf) {
+        payload_ref = av_buffer_ref(payload_buf);
+        if (!payload_ref)
+            return AVERROR(ENOMEM);
+    } else {
+        payload_ref = NULL;
+    }
+
+    // Find an existing SEI unit or make a new one to add to.
+    err = cbs_sei_get_unit(ctx, au, prefix, &unit);
+    if (err < 0)
+        return err;
+
+    // Find the message list inside the codec-dependent unit.
+    err = cbs_sei_get_message_list(ctx, unit, &list);
+    if (err < 0)
+        return err;
+
+    // Add a new message to the message list.
+    err = ff_cbs_sei_list_add(list);
+    if (err < 0)
+        return err;
+
+    message = &list->messages[list->nb_messages - 1];
+
+    message->payload_type = payload_type;
+    message->payload      = payload_data;
+    message->payload_ref  = payload_ref;
+
+    return 0;
+}
+
+int ff_cbs_sei_find_message(CodedBitstreamContext *ctx,
+                            CodedBitstreamFragment *au,
+                            uint32_t payload_type,
+                            SEIRawMessage **iter)
+{
+    int err, i, j, found;
+
+    found = 0;
+    for (i = 0; i < au->nb_units; i++) {
+        CodedBitstreamUnit *unit = &au->units[i];
+        SEIRawMessageList *list;
+
+        err = cbs_sei_get_message_list(ctx, unit, &list);
+        if (err < 0)
+            continue;
+
+        for (j = 0; j < list->nb_messages; j++) {
+            SEIRawMessage *message = &list->messages[j];
+
+            if (message->payload_type == payload_type) {
+                if (!*iter || found) {
+                    *iter = message;
+                    return 0;
+                }
+                if (message == *iter)
+                    found = 1;
+            }
+        }
+    }
+
+    return AVERROR(ENOENT);
+}
+
+static void cbs_sei_delete_message(SEIRawMessageList *list,
+                                   int position)
+{
+    SEIRawMessage *message;
+
+    av_assert0(0 <= position && position < list->nb_messages);
+
+    message = &list->messages[position];
+    av_buffer_unref(&message->payload_ref);
+    av_buffer_unref(&message->extension_data_ref);
+
+    --list->nb_messages;
+
+    if (list->nb_messages > 0) {
+        memmove(list->messages + position,
+                list->messages + position + 1,
+                (list->nb_messages - position) * sizeof(*list->messages));
+    }
+}
+
+void ff_cbs_sei_delete_message_type(CodedBitstreamContext *ctx,
+                                    CodedBitstreamFragment *au,
+                                    uint32_t payload_type)
+{
+    int err, i, j;
+
+    for (i = 0; i < au->nb_units; i++) {
+        CodedBitstreamUnit *unit = &au->units[i];
+        SEIRawMessageList *list;
+
+        err = cbs_sei_get_message_list(ctx, unit, &list);
+        if (err < 0)
+            continue;
+
+        for (j = 0; j < list->nb_messages;) {
+            if (list->messages[j].payload_type == payload_type)
+                cbs_sei_delete_message(list, j);
+            else
+                ++j;
+        }
+    }
+}
diff --git a/libavcodec/cbs_sei.h b/libavcodec/cbs_sei.h
index 95beabf4d7..5ce4ad3ccd 100644
--- a/libavcodec/cbs_sei.h
+++ b/libavcodec/cbs_sei.h
@@ -21,8 +21,132 @@ 
 
 #include <stddef.h>
 #include <stdint.h>
+
 #include "libavutil/buffer.h"
 
+#include "cbs.h"
+
+// SEI payload types form a common namespace between the H.264, H.265
+// and H.266 standards.  A given payload type always has the same
+// meaning, but some names have different payload types in different
+// standards (e.g. scalable-nesting is 30 in H.264 but 133 in H.265).
+// The content of the payload data depends on the standard, though
+// many generic parts have the same interpretation everywhere (such as
+// mastering-display-colour-volume and user-data-unregistered).
+enum {
+    SEI_TYPE_BUFFERING_PERIOD                            = 0,
+    SEI_TYPE_PIC_TIMING                                  = 1,
+    SEI_TYPE_PAN_SCAN_RECT                               = 2,
+    SEI_TYPE_FILLER_PAYLOAD                              = 3,
+    SEI_TYPE_USER_DATA_REGISTERED_ITU_T_T35              = 4,
+    SEI_TYPE_USER_DATA_UNREGISTERED                      = 5,
+    SEI_TYPE_RECOVERY_POINT                              = 6,
+    SEI_TYPE_DEC_REF_PIC_MARKING_REPETITION              = 7,
+    SEI_TYPE_SPARE_PIC                                   = 8,
+    SEI_TYPE_SCENE_INFO                                  = 9,
+    SEI_TYPE_SUB_SEQ_INFO                                = 10,
+    SEI_TYPE_SUB_SEQ_LAYER_CHARACTERISTICS               = 11,
+    SEI_TYPE_SUB_SEQ_CHARACTERISTICS                     = 12,
+    SEI_TYPE_FULL_FRAME_FREEZE                           = 13,
+    SEI_TYPE_FULL_FRAME_FREEZE_RELEASE                   = 14,
+    SEI_TYPE_FULL_FRAME_SNAPSHOT                         = 15,
+    SEI_TYPE_PROGRESSIVE_REFINEMENT_SEGMENT_START        = 16,
+    SEI_TYPE_PROGRESSIVE_REFINEMENT_SEGMENT_END          = 17,
+    SEI_TYPE_MOTION_CONSTRAINED_SLICE_GROUP_SET          = 18,
+    SEI_TYPE_FILM_GRAIN_CHARACTERISTICS                  = 19,
+    SEI_TYPE_DEBLOCKING_FILTER_DISPLAY_PREFERENCE        = 20,
+    SEI_TYPE_STEREO_VIDEO_INFO                           = 21,
+    SEI_TYPE_POST_FILTER_HINT                            = 22,
+    SEI_TYPE_TONE_MAPPING_INFO                           = 23,
+    SEI_TYPE_SCALABILITY_INFO                            = 24,
+    SEI_TYPE_SUB_PIC_SCALABLE_LAYER                      = 25,
+    SEI_TYPE_NON_REQUIRED_LAYER_REP                      = 26,
+    SEI_TYPE_PRIORITY_LAYER_INFO                         = 27,
+    SEI_TYPE_LAYERS_NOT_PRESENT_4                        = 28,
+    SEI_TYPE_LAYER_DEPENDENCY_CHANGE                     = 29,
+    SEI_TYPE_SCALABLE_NESTING_4                          = 30,
+    SEI_TYPE_BASE_LAYER_TEMPORAL_HRD                     = 31,
+    SEI_TYPE_QUALITY_LAYER_INTEGRITY_CHECK               = 32,
+    SEI_TYPE_REDUNDANT_PIC_PROPERTY                      = 33,
+    SEI_TYPE_TL0_DEP_REP_INDEX                           = 34,
+    SEI_TYPE_TL_SWITCHING_POINT                          = 35,
+    SEI_TYPE_PARALLEL_DECODING_INFO                      = 36,
+    SEI_TYPE_MVC_SCALABLE_NESTING                        = 37,
+    SEI_TYPE_VIEW_SCALABILITY_INFO                       = 38,
+    SEI_TYPE_MULTIVIEW_SCENE_INFO_4                      = 39,
+    SEI_TYPE_MULTIVIEW_ACQUISITION_INFO_4                = 40,
+    SEI_TYPE_NON_REQUIRED_VIEW_COMPONENT                 = 41,
+    SEI_TYPE_VIEW_DEPENDENCY_CHANGE                      = 42,
+    SEI_TYPE_OPERATION_POINTS_NOT_PRESENT                = 43,
+    SEI_TYPE_BASE_VIEW_TEMPORAL_HRD                      = 44,
+    SEI_TYPE_FRAME_PACKING_ARRANGEMENT                   = 45,
+    SEI_TYPE_MULTIVIEW_VIEW_POSITION_4                   = 46,
+    SEI_TYPE_DISPLAY_ORIENTATION                         = 47,
+    SEI_TYPE_MVCD_SCALABLE_NESTING                       = 48,
+    SEI_TYPE_MVCD_VIEW_SCALABILITY_INFO                  = 49,
+    SEI_TYPE_DEPTH_REPRESENTATION_INFO_4                 = 50,
+    SEI_TYPE_THREE_DIMENSIONAL_REFERENCE_DISPLAYS_INFO_4 = 51,
+    SEI_TYPE_DEPTH_TIMING                                = 52,
+    SEI_TYPE_DEPTH_SAMPLING_INFO                         = 53,
+    SEI_TYPE_CONSTRAINED_DEPTH_PARAMETER_SET_IDENTIFIER  = 54,
+    SEI_TYPE_GREEN_METADATA                              = 56,
+    SEI_TYPE_STRUCTURE_OF_PICTURES_INFO                  = 128,
+    SEI_TYPE_ACTIVE_PARAMETER_SETS                       = 129,
+    SEI_TYPE_DECODING_UNIT_INFO                          = 130,
+    SEI_TYPE_TEMPORAL_SUB_LAYER_ZERO_IDX                 = 131,
+    SEI_TYPE_DECODED_PICTURE_HASH                        = 132,
+    SEI_TYPE_SCALABLE_NESTING_5                          = 133,
+    SEI_TYPE_REGION_REFRESH_INFO                         = 134,
+    SEI_TYPE_NO_DISPLAY                                  = 135,
+    SEI_TYPE_TIME_CODE                                   = 136,
+    SEI_TYPE_MASTERING_DISPLAY_COLOUR_VOLUME             = 137,
+    SEI_TYPE_SEGMENTED_RECT_FRAME_PACKING_ARRANGEMENT    = 138,
+    SEI_TYPE_TEMPORAL_MOTION_CONSTRAINED_TILE_SETS       = 139,
+    SEI_TYPE_CHROMA_RESAMPLING_FILTER_HINT               = 140,
+    SEI_TYPE_KNEE_FUNCTION_INFO                          = 141,
+    SEI_TYPE_COLOUR_REMAPPING_INFO                       = 142,
+    SEI_TYPE_DEINTERLACED_FIELD_IDENTIFICATION           = 143,
+    SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO                    = 144,
+    SEI_TYPE_DEPENDENT_RAP_INDICATION                    = 145,
+    SEI_TYPE_CODED_REGION_COMPLETION                     = 146,
+    SEI_TYPE_ALTERNATIVE_TRANSFER_CHARACTERISTICS        = 147,
+    SEI_TYPE_AMBIENT_VIEWING_ENVIRONMENT                 = 148,
+    SEI_TYPE_CONTENT_COLOUR_VOLUME                       = 149,
+    SEI_TYPE_EQUIRECTANGULAR_PROJECTION                  = 150,
+    SEI_TYPE_CUBEMAP_PROJECTION                          = 151,
+    SEI_TYPE_FISHEYE_VIDEO_INFO                          = 152,
+    SEI_TYPE_SPHERE_ROTATION                             = 154,
+    SEI_TYPE_REGIONWISE_PACKING                          = 155,
+    SEI_TYPE_OMNI_VIEWPORT                               = 156,
+    SEI_TYPE_REGIONAL_NESTING                            = 157,
+    SEI_TYPE_MCTS_EXTRACTION_INFO_SETS                   = 158,
+    SEI_TYPE_MCTS_EXTRACTION_INFO_NESTING                = 159,
+    SEI_TYPE_LAYERS_NOT_PRESENT_5                        = 160,
+    SEI_TYPE_INTER_LAYER_CONSTRAINED_TILE_SETS           = 161,
+    SEI_TYPE_BSP_NESTING                                 = 162,
+    SEI_TYPE_BSP_INITIAL_ARRIVAL_TIME                    = 163,
+    SEI_TYPE_SUB_BITSTREAM_PROPERTY                      = 164,
+    SEI_TYPE_ALPHA_CHANNEL_INFO                          = 165,
+    SEI_TYPE_OVERLAY_INFO                                = 166,
+    SEI_TYPE_TEMPORAL_MV_PREDICTION_CONSTRAINTS          = 167,
+    SEI_TYPE_FRAME_FIELD_INFO                            = 168,
+    SEI_TYPE_THREE_DIMENSIONAL_REFERENCE_DISPLAYS_INFO   = 176,
+    SEI_TYPE_DEPTH_REPRESENTATION_INFO_5                 = 177,
+    SEI_TYPE_MULTIVIEW_SCENE_INFO_5                      = 178,
+    SEI_TYPE_MULTIVIEW_ACQUISITION_INFO_5                = 179,
+    SEI_TYPE_MULTIVIEW_VIEW_POSITION_5                   = 180,
+    SEI_TYPE_ALTERNATIVE_DEPTH_INFO                      = 181,
+    SEI_TYPE_SEI_MANIFEST                                = 200,
+    SEI_TYPE_SEI_PREFIX_INDICATION                       = 201,
+    SEI_TYPE_ANNOTATED_REGIONS                           = 202,
+    SEI_TYPE_SUBPIC_LEVEL_INFO                           = 203,
+    SEI_TYPE_SAMPLE_ASPECT_RATIO_INFO                    = 204,
+};
+
+
+typedef struct SEIRawFillerPayload {
+    uint32_t payload_size;
+} SEIRawFillerPayload;
 
 typedef struct SEIRawUserDataRegistered {
     uint8_t      itu_t_t35_country_code;
@@ -57,4 +181,135 @@  typedef struct SEIRawAlternativeTransferCharacteristics {
     uint8_t preferred_transfer_characteristics;
 } SEIRawAlternativeTransferCharacteristics;
 
+typedef struct SEIRawMessage {
+    uint32_t     payload_type;
+    uint32_t     payload_size;
+    void        *payload;
+    AVBufferRef *payload_ref;
+    uint8_t     *extension_data;
+    AVBufferRef *extension_data_ref;
+    size_t       extension_bit_length;
+} SEIRawMessage;
+
+typedef struct SEIRawMessageList {
+    SEIRawMessage *messages;
+    int         nb_messages;
+    int         nb_messages_allocated;
+} SEIRawMessageList;
+
+
+typedef struct SEIMessageState {
+    // 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;
+} SEIMessageState;
+
+struct GetBitContext;
+struct PutBitContext;
+
+typedef int (*SEIMessageReadFunction)(CodedBitstreamContext *ctx,
+                                      struct GetBitContext *rw,
+                                      void *current,
+                                      SEIMessageState *sei);
+
+typedef int (*SEIMessageWriteFunction)(CodedBitstreamContext *ctx,
+                                       struct PutBitContext *rw,
+                                       void *current,
+                                       SEIMessageState *sei);
+
+typedef struct SEIMessageTypeDescriptor {
+    // Payload type for the message.  (-1 in this field ends a list.)
+    int     type;
+    // Valid in a prefix SEI NAL unit (always for H.264).
+    uint8_t prefix;
+    // Valid in a suffix SEI NAL unit (never for H.264).
+    uint8_t suffix;
+    // Size of the decomposed structure.
+    size_t  size;
+    // Read bitstream into SEI message.
+    SEIMessageReadFunction  read;
+    // Write bitstream from SEI message.
+    SEIMessageWriteFunction write;
+} SEIMessageTypeDescriptor;
+
+// Macro for the read/write pair.  The clumsy cast is needed because the
+// current pointer is typed in all of the read/write functions but has to
+// be void here to fit all cases.
+#define SEI_MESSAGE_RW(codec, name) \
+    .read  = (SEIMessageReadFunction) cbs_ ## codec ## _read_  ## name, \
+    .write = (SEIMessageWriteFunction)cbs_ ## codec ## _write_ ## name
+
+// End-of-list sentinel element.
+#define SEI_MESSAGE_TYPE_END { .type = -1 }
+
+
+/**
+ * Find the type descriptor for the given payload type.
+ *
+ * Returns NULL if the payload type is not known.
+ */
+const SEIMessageTypeDescriptor *ff_cbs_sei_find_type(CodedBitstreamContext *ctx,
+                                                     int payload_type);
+
+/**
+ * Allocate a new payload for the given SEI message.
+ */
+int ff_cbs_sei_alloc_message_payload(SEIRawMessage *message,
+                                     const SEIMessageTypeDescriptor *desc);
+
+/**
+ * Allocate a new empty SEI message in a message list.
+ *
+ * The new message is in place nb_messages - 1.
+ */
+int ff_cbs_sei_list_add(SEIRawMessageList *list);
+
+/**
+ * Free all SEI messages in a message list.
+ */
+void ff_cbs_sei_free_message_list(SEIRawMessageList *list);
+
+/**
+ * Add an SEI message to an access unit.
+ *
+ * Will add to an existing SEI NAL unit, or create a new one for the
+ * message if there is no suitable existing one.
+ *
+ * Takes a new reference to payload_buf, if set.  If payload_buf is
+ * NULL then the new message will not be reference counted.
+ */
+int ff_cbs_sei_add_message(CodedBitstreamContext *ctx,
+                           CodedBitstreamFragment *au,
+                           int prefix,
+                           uint32_t     payload_type,
+                           void        *payload_data,
+                           AVBufferRef *payload_buf);
+
+/**
+ * Iterate over messages with the given payload type in an access unit.
+ *
+ * Set message to NULL in the first call.  Returns 0 while more messages
+ * are available, AVERROR(ENOENT) when all messages have been found.
+ */
+int ff_cbs_sei_find_message(CodedBitstreamContext *ctx,
+                            CodedBitstreamFragment *au,
+                            uint32_t payload_type,
+                            SEIRawMessage **message);
+
+/**
+ * Delete all messages with the given payload type from an access unit.
+ */
+void ff_cbs_sei_delete_message_type(CodedBitstreamContext *ctx,
+                                    CodedBitstreamFragment *au,
+                                    uint32_t payload_type);
+
 #endif /* AVCODEC_CBS_SEI_H */
diff --git a/libavcodec/cbs_sei_syntax_template.c b/libavcodec/cbs_sei_syntax_template.c
index cc900830ae..1a0516acce 100644
--- a/libavcodec/cbs_sei_syntax_template.c
+++ b/libavcodec/cbs_sei_syntax_template.c
@@ -16,9 +16,27 @@ 
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-static int FUNC(sei_user_data_registered)
+static int FUNC(filler_payload)
     (CodedBitstreamContext *ctx, RWContext *rw,
-     SEIRawUserDataRegistered *current, uint32_t *payload_size)
+     SEIRawFillerPayload *current, SEIMessageState *state)
+{
+    int err, i;
+
+    HEADER("Filler Payload");
+
+#ifdef READ
+    current->payload_size = state->payload_size;
+#endif
+
+    for (i = 0; i < current->payload_size; i++)
+        fixed(8, ff_byte, 0xff);
+
+    return 0;
+}
+
+static int FUNC(user_data_registered)
+    (CodedBitstreamContext *ctx, RWContext *rw,
+     SEIRawUserDataRegistered *current, SEIMessageState *state)
 {
     int err, i, j;
 
@@ -33,14 +51,12 @@  static int FUNC(sei_user_data_registered)
     }
 
 #ifdef READ
-    if (*payload_size < i) {
+    if (state->payload_size < i) {
         av_log(ctx->log_ctx, AV_LOG_ERROR,
                "Invalid SEI user data registered payload.\n");
         return AVERROR_INVALIDDATA;
     }
-    current->data_length = *payload_size - i;
-#else
-    *payload_size = i + current->data_length;
+    current->data_length = state->payload_size - i;
 #endif
 
     allocate(current->data, current->data_length);
@@ -50,23 +66,21 @@  static int FUNC(sei_user_data_registered)
     return 0;
 }
 
-static int FUNC(sei_user_data_unregistered)
+static int FUNC(user_data_unregistered)
     (CodedBitstreamContext *ctx, RWContext *rw,
-     SEIRawUserDataUnregistered *current, uint32_t *payload_size)
+     SEIRawUserDataUnregistered *current, SEIMessageState *state)
 {
     int err, i;
 
     HEADER("User Data Unregistered");
 
 #ifdef READ
-    if (*payload_size < 16) {
+    if (state->payload_size < 16) {
         av_log(ctx->log_ctx, AV_LOG_ERROR,
                "Invalid SEI user data unregistered payload.\n");
         return AVERROR_INVALIDDATA;
     }
-    current->data_length = *payload_size - 16;
-#else
-    *payload_size = 16 + current->data_length;
+    current->data_length = state->payload_size - 16;
 #endif
 
     for (i = 0; i < 16; i++)
@@ -80,9 +94,9 @@  static int FUNC(sei_user_data_unregistered)
     return 0;
 }
 
-static int FUNC(sei_mastering_display_colour_volume)
+static int FUNC(mastering_display_colour_volume)
     (CodedBitstreamContext *ctx, RWContext *rw,
-     SEIRawMasteringDisplayColourVolume *current)
+     SEIRawMasteringDisplayColourVolume *current, SEIMessageState *state)
 {
     int err, c;
 
@@ -104,13 +118,13 @@  static int FUNC(sei_mastering_display_colour_volume)
     return 0;
 }
 
-static int FUNC(sei_content_light_level)
+static int FUNC(content_light_level_info)
     (CodedBitstreamContext *ctx, RWContext *rw,
-     SEIRawContentLightLevelInfo *current)
+     SEIRawContentLightLevelInfo *current, SEIMessageState *state)
 {
     int err;
 
-    HEADER("Content Light Level");
+    HEADER("Content Light Level Information");
 
     ub(16, max_content_light_level);
     ub(16, max_pic_average_light_level);
@@ -118,9 +132,10 @@  static int FUNC(sei_content_light_level)
     return 0;
 }
 
-static int FUNC(sei_alternative_transfer_characteristics)
+static int FUNC(alternative_transfer_characteristics)
     (CodedBitstreamContext *ctx, RWContext *rw,
-     SEIRawAlternativeTransferCharacteristics *current)
+     SEIRawAlternativeTransferCharacteristics *current,
+     SEIMessageState *state)
 {
     int err;
 
@@ -130,3 +145,165 @@  static int FUNC(sei_alternative_transfer_characteristics)
 
     return 0;
 }
+
+static int FUNC(message)(CodedBitstreamContext *ctx, RWContext *rw,
+                         SEIRawMessage *current)
+{
+    const SEIMessageTypeDescriptor *desc;
+    int err, i;
+
+    desc = ff_cbs_sei_find_type(ctx, current->payload_type);
+    if (desc) {
+        SEIMessageState 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_sei_alloc_message_payload(current, desc));
+#endif
+
+        start_position = bit_position(rw);
+
+        CHECK(desc->READWRITE(ctx, rw, current->payload, &state));
+
+        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 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, bit_equal_to_one, 1);
+            while (byte_alignment(rw))
+                fixed(1, bit_equal_to_zero, 0);
+        }
+
+#ifdef WRITE
+        current->payload_size = (put_bits_count(rw) - start_position) / 8;
+#endif
+    } else {
+        uint8_t *data;
+
+        allocate(current->payload, current->payload_size);
+        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(message_list)(CodedBitstreamContext *ctx, RWContext *rw,
+                              SEIRawMessageList *current, int prefix)
+{
+    SEIRawMessage *message;
+    int err, k;
+
+#ifdef READ
+    for (k = 0;; k++) {
+        uint32_t payload_type = 0;
+        uint32_t payload_size = 0;
+        uint32_t tmp;
+
+        while (show_bits(rw, 8) == 0xff) {
+            fixed(8, ff_byte, 0xff);
+            payload_type += 255;
+        }
+        xu(8, last_payload_type_byte, tmp, 0, 254, 0);
+        payload_type += tmp;
+
+        while (show_bits(rw, 8) == 0xff) {
+            fixed(8, ff_byte, 0xff);
+            payload_size += 255;
+        }
+        xu(8, last_payload_size_byte, tmp, 0, 254, 0);
+        payload_size += tmp;
+
+        CHECK(ff_cbs_sei_list_add(current));
+        message = &current->messages[k];
+
+        message->payload_type = payload_type;
+        message->payload_size = payload_size;
+
+        CHECK(FUNC(message)(ctx, rw, message));
+
+        if (!cbs_h2645_read_more_rbsp_data(rw))
+            break;
+    }
+#else
+    for (k = 0; k < current->nb_messages; k++) {
+        PutBitContext start_state;
+        uint32_t tmp;
+        int trace, i;
+
+        message = &current->messages[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 = message->payload_type;
+            while (tmp >= 255) {
+                fixed(8, ff_byte, 0xff);
+                tmp -= 255;
+            }
+            xu(8, last_payload_type_byte, tmp, 0, 254, 0);
+
+            tmp = message->payload_size;
+            while (tmp >= 255) {
+                fixed(8, ff_byte, 0xff);
+                tmp -= 255;
+            }
+            xu(8, last_payload_size_byte, tmp, 0, 254, 0);
+
+            err = FUNC(message)(ctx, rw, message);
+            ctx->trace_enable = trace;
+            if (err < 0)
+                return err;
+        }
+    }
+#endif
+
+    return 0;
+}
diff --git a/libavcodec/h264_metadata_bsf.c b/libavcodec/h264_metadata_bsf.c
index f39e649ac6..4ab97aee3a 100644
--- a/libavcodec/h264_metadata_bsf.c
+++ b/libavcodec/h264_metadata_bsf.c
@@ -78,13 +78,14 @@  typedef struct H264MetadataContext {
     int crop_bottom;
 
     const char *sei_user_data;
-    H264RawSEIPayload sei_user_data_payload;
+    SEIRawUserDataUnregistered sei_user_data_payload;
 
     int delete_filler;
 
     int display_orientation;
     double rotate;
     int flip;
+    H264RawSEIDisplayOrientation display_orientation_payload;
 
     int level;
 } H264MetadataContext;
@@ -414,7 +415,9 @@  static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *pkt)
     // Only insert the SEI in access units containing SPSs, and also
     // unconditionally in the first access unit we ever see.
     if (ctx->sei_user_data && (has_sps || !ctx->done_first_au)) {
-        err = ff_cbs_h264_add_sei_message(au, &ctx->sei_user_data_payload);
+        err = ff_cbs_sei_add_message(ctx->output, au, 1,
+                                     H264_SEI_TYPE_USER_DATA_UNREGISTERED,
+                                     &ctx->sei_user_data_payload, NULL);
         if (err < 0) {
             av_log(bsf, AV_LOG_ERROR, "Failed to add user data SEI "
                    "message to access unit.\n");
@@ -428,74 +431,54 @@  static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *pkt)
                 ff_cbs_delete_unit(au, i);
                 continue;
             }
-
-            if (au->units[i].type == H264_NAL_SEI) {
-                // Filler SEI messages.
-                H264RawSEI *sei = au->units[i].content;
-
-                for (j = sei->payload_count - 1; j >= 0; j--) {
-                    if (sei->payload[j].payload_type ==
-                        H264_SEI_TYPE_FILLER_PAYLOAD)
-                        ff_cbs_h264_delete_sei_message(au, &au->units[i], j);
-                }
-            }
         }
+
+        ff_cbs_sei_delete_message_type(ctx->output, au,
+                                       H264_SEI_TYPE_FILLER_PAYLOAD);
     }
 
     if (ctx->display_orientation != PASS) {
-        for (i = au->nb_units - 1; i >= 0; i--) {
-            H264RawSEI *sei;
-            if (au->units[i].type != H264_NAL_SEI)
-                continue;
-            sei = au->units[i].content;
-
-            for (j = sei->payload_count - 1; j >= 0; j--) {
-                H264RawSEIDisplayOrientation *disp;
-                int32_t *matrix;
-
-                if (sei->payload[j].payload_type !=
-                    H264_SEI_TYPE_DISPLAY_ORIENTATION)
-                    continue;
-                disp = &sei->payload[j].payload.display_orientation;
-
-                if (ctx->display_orientation == REMOVE ||
-                    ctx->display_orientation == INSERT) {
-                    ff_cbs_h264_delete_sei_message(au, &au->units[i], j);
-                    continue;
-                }
-
-                matrix = av_malloc(9 * sizeof(int32_t));
-                if (!matrix) {
-                    err = AVERROR(ENOMEM);
-                    goto fail;
-                }
+        SEIRawMessage *message = NULL;
+        while (ff_cbs_sei_find_message(ctx->output, au,
+                                       H264_SEI_TYPE_DISPLAY_ORIENTATION,
+                                       &message) == 0) {
+            H264RawSEIDisplayOrientation *disp = message->payload;
+            int32_t *matrix;
+
+            matrix = av_malloc(9 * sizeof(int32_t));
+            if (!matrix) {
+                err = AVERROR(ENOMEM);
+                goto fail;
+            }
 
-                av_display_rotation_set(matrix,
-                                        disp->anticlockwise_rotation *
-                                        180.0 / 65536.0);
-                av_display_matrix_flip(matrix, disp->hor_flip, disp->ver_flip);
-
-                // If there are multiple display orientation messages in an
-                // access unit, then the last one added to the packet (i.e.
-                // the first one in the access unit) will prevail.
-                err = av_packet_add_side_data(pkt, AV_PKT_DATA_DISPLAYMATRIX,
-                                              (uint8_t*)matrix,
-                                              9 * sizeof(int32_t));
-                if (err < 0) {
-                    av_log(bsf, AV_LOG_ERROR, "Failed to attach extracted "
-                           "displaymatrix side data to packet.\n");
-                    av_free(matrix);
-                    goto fail;
-                }
+            av_display_rotation_set(matrix,
+                                    disp->anticlockwise_rotation *
+                                    180.0 / 65536.0);
+            av_display_matrix_flip(matrix, disp->hor_flip, disp->ver_flip);
+
+            // If there are multiple display orientation messages in an
+            // access unit, then the last one added to the packet (i.e.
+            // the first one in the access unit) will prevail.
+            err = av_packet_add_side_data(pkt, AV_PKT_DATA_DISPLAYMATRIX,
+                                          (uint8_t*)matrix,
+                                          9 * sizeof(int32_t));
+            if (err < 0) {
+                av_log(bsf, AV_LOG_ERROR, "Failed to attach extracted "
+                       "displaymatrix side data to packet.\n");
+                av_free(matrix);
+                goto fail;
             }
         }
+
+        if (ctx->display_orientation == REMOVE ||
+            ctx->display_orientation == INSERT) {
+            ff_cbs_sei_delete_message_type(ctx->output, au,
+                                           H264_SEI_TYPE_DISPLAY_ORIENTATION);
+        }
     }
     if (ctx->display_orientation == INSERT) {
-        H264RawSEIPayload payload = {
-            .payload_type = H264_SEI_TYPE_DISPLAY_ORIENTATION,
-        };
         H264RawSEIDisplayOrientation *disp =
-            &payload.payload.display_orientation;
+            &ctx->display_orientation_payload;
         uint8_t *data;
         int size;
         int write = 0;
@@ -551,7 +534,9 @@  static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *pkt)
         if (write) {
             disp->display_orientation_repetition_period = 1;
 
-            err = ff_cbs_h264_add_sei_message(au, &payload);
+            err = ff_cbs_sei_add_message(ctx->output, au, 1,
+                                         H264_SEI_TYPE_DISPLAY_ORIENTATION,
+                                         disp, NULL);
             if (err < 0) {
                 av_log(bsf, AV_LOG_ERROR, "Failed to add display orientation "
                        "SEI message to access unit.\n");
@@ -585,13 +570,9 @@  static int h264_metadata_init(AVBSFContext *bsf)
     int err, i;
 
     if (ctx->sei_user_data) {
-        SEIRawUserDataUnregistered *udu =
-            &ctx->sei_user_data_payload.payload.user_data_unregistered;
+        SEIRawUserDataUnregistered *udu = &ctx->sei_user_data_payload;
         int j;
 
-        ctx->sei_user_data_payload.payload_type =
-            H264_SEI_TYPE_USER_DATA_UNREGISTERED;
-
         // Parse UUID.  It must be a hex string of length 32, possibly
         // containing '-'s between hex digits (which we ignore).
         for (i = j = 0; j < 32 && i < 64 && ctx->sei_user_data[i]; i++) {
diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c
index b577d09caf..d24462414c 100644
--- a/libavcodec/vaapi_encode_h264.c
+++ b/libavcodec/vaapi_encode_h264.c
@@ -90,7 +90,6 @@  typedef struct VAAPIEncodeH264Context {
     H264RawAUD   raw_aud;
     H264RawSPS   raw_sps;
     H264RawPPS   raw_pps;
-    H264RawSEI   raw_sei;
     H264RawSlice raw_slice;
 
     H264RawSEIBufferingPeriod      sei_buffering_period;
@@ -210,11 +209,9 @@  static int vaapi_encode_h264_write_extra_header(AVCodecContext *avctx,
 {
     VAAPIEncodeH264Context *priv = avctx->priv_data;
     CodedBitstreamFragment   *au = &priv->current_access_unit;
-    int err, i;
+    int err;
 
     if (priv->sei_needed) {
-        H264RawSEI *sei = &priv->raw_sei;
-
         if (priv->aud_needed) {
             err = vaapi_encode_h264_add_nal(avctx, au, &priv->raw_aud);
             if (err < 0)
@@ -222,41 +219,35 @@  static int vaapi_encode_h264_write_extra_header(AVCodecContext *avctx,
             priv->aud_needed = 0;
         }
 
-        *sei = (H264RawSEI) {
-            .nal_unit_header = {
-                .nal_unit_type = H264_NAL_SEI,
-            },
-        };
-
-        i = 0;
-
         if (priv->sei_needed & SEI_IDENTIFIER) {
-            sei->payload[i].payload_type = H264_SEI_TYPE_USER_DATA_UNREGISTERED;
-            sei->payload[i].payload.user_data_unregistered = priv->sei_identifier;
-            ++i;
+            err = ff_cbs_sei_add_message(priv->cbc, au, 1,
+                                         H264_SEI_TYPE_USER_DATA_UNREGISTERED,
+                                         &priv->sei_identifier, NULL);
+            if (err < 0)
+                goto fail;
         }
         if (priv->sei_needed & SEI_TIMING) {
             if (pic->type == PICTURE_TYPE_IDR) {
-                sei->payload[i].payload_type = H264_SEI_TYPE_BUFFERING_PERIOD;
-                sei->payload[i].payload.buffering_period = priv->sei_buffering_period;
-                ++i;
+                err = ff_cbs_sei_add_message(priv->cbc, au, 1,
+                                             H264_SEI_TYPE_BUFFERING_PERIOD,
+                                             &priv->sei_buffering_period, NULL);
+                if (err < 0)
+                    goto fail;
             }
-            sei->payload[i].payload_type = H264_SEI_TYPE_PIC_TIMING;
-            sei->payload[i].payload.pic_timing = priv->sei_pic_timing;
-            ++i;
+            err = ff_cbs_sei_add_message(priv->cbc, au, 1,
+                                         H264_SEI_TYPE_PIC_TIMING,
+                                         &priv->sei_pic_timing, NULL);
+            if (err < 0)
+                goto fail;
         }
         if (priv->sei_needed & SEI_RECOVERY_POINT) {
-            sei->payload[i].payload_type = H264_SEI_TYPE_RECOVERY_POINT;
-            sei->payload[i].payload.recovery_point = priv->sei_recovery_point;
-            ++i;
+            err = ff_cbs_sei_add_message(priv->cbc, au, 1,
+                                         H264_SEI_TYPE_RECOVERY_POINT,
+                                         &priv->sei_recovery_point, NULL);
+            if (err < 0)
+                goto fail;
         }
 
-        sei->payload_count = i;
-        av_assert0(sei->payload_count > 0);
-
-        err = vaapi_encode_h264_add_nal(avctx, au, sei);
-        if (err < 0)
-            goto fail;
         priv->sei_needed = 0;
 
         err = vaapi_encode_h264_write_access_unit(avctx, data, data_len, au);
diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c
index a7af763ae4..2e8e772008 100644
--- a/libavcodec/vaapi_encode_h265.c
+++ b/libavcodec/vaapi_encode_h265.c
@@ -73,7 +73,6 @@  typedef struct VAAPIEncodeH265Context {
     H265RawVPS   raw_vps;
     H265RawSPS   raw_sps;
     H265RawPPS   raw_pps;
-    H265RawSEI   raw_sei;
     H265RawSlice raw_slice;
 
     SEIRawMasteringDisplayColourVolume sei_mastering_display;
@@ -195,11 +194,9 @@  static int vaapi_encode_h265_write_extra_header(AVCodecContext *avctx,
 {
     VAAPIEncodeH265Context *priv = avctx->priv_data;
     CodedBitstreamFragment   *au = &priv->current_access_unit;
-    int err, i;
+    int err;
 
     if (priv->sei_needed) {
-        H265RawSEI *sei = &priv->raw_sei;
-
         if (priv->aud_needed) {
             err = vaapi_encode_h265_add_nal(avctx, au, &priv->aud);
             if (err < 0)
@@ -207,35 +204,22 @@  static int vaapi_encode_h265_write_extra_header(AVCodecContext *avctx,
             priv->aud_needed = 0;
         }
 
-        *sei = (H265RawSEI) {
-            .nal_unit_header = {
-                .nal_unit_type         = HEVC_NAL_SEI_PREFIX,
-                .nuh_layer_id          = 0,
-                .nuh_temporal_id_plus1 = 1,
-            },
-        };
-
-        i = 0;
-
         if (priv->sei_needed & SEI_MASTERING_DISPLAY) {
-            sei->payload[i].payload_type = HEVC_SEI_TYPE_MASTERING_DISPLAY_INFO;
-            sei->payload[i].payload.mastering_display_colour_volume =
-                priv->sei_mastering_display;
-            ++i;
+            err = ff_cbs_sei_add_message(priv->cbc, au, 1,
+                                         HEVC_SEI_TYPE_MASTERING_DISPLAY_INFO,
+                                         &priv->sei_mastering_display, NULL);
+            if (err < 0)
+                goto fail;
         }
 
         if (priv->sei_needed & SEI_CONTENT_LIGHT_LEVEL) {
-            sei->payload[i].payload_type = HEVC_SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO;
-            sei->payload[i].payload.content_light_level = priv->sei_content_light_level;
-            ++i;
+            err = ff_cbs_sei_add_message(priv->cbc, au, 1,
+                                         HEVC_SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO,
+                                         &priv->sei_content_light_level, NULL);
+            if (err < 0)
+                goto fail;
         }
 
-        sei->payload_count = i;
-        av_assert0(sei->payload_count > 0);
-
-        err = vaapi_encode_h265_add_nal(avctx, au, sei);
-        if (err < 0)
-            goto fail;
         priv->sei_needed = 0;
 
         err = vaapi_encode_h265_write_access_unit(avctx, data, data_len, au);