[FFmpeg-devel] [PATCH 03/27] cbs_h2645: Merge SEI message handling in common between codecs
Nuo Mi
nuomi2021 at gmail.com
Mon Jan 4 17:42:23 EET 2021
On Sat, Jan 2, 2021 at 5:44 AM Mark Thompson <sw at 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, ¤t->payload.buffering_period));
> - break;
> - case H264_SEI_TYPE_PIC_TIMING:
> - CHECK(FUNC(sei_pic_timing)
> - (ctx, rw, ¤t->payload.pic_timing));
> - break;
> - case H264_SEI_TYPE_PAN_SCAN_RECT:
> - CHECK(FUNC(sei_pan_scan_rect)
> - (ctx, rw, ¤t->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, ¤t->payload.user_data_registered,
> ¤t->payload_size));
> - break;
> - case H264_SEI_TYPE_USER_DATA_UNREGISTERED:
> - CHECK(FUNC_SEI(sei_user_data_unregistered)
> - (ctx, rw, ¤t->payload.user_data_unregistered,
> ¤t->payload_size));
> - break;
> - case H264_SEI_TYPE_RECOVERY_POINT:
> - CHECK(FUNC(sei_recovery_point)
> - (ctx, rw, ¤t->payload.recovery_point));
> - break;
> - case H264_SEI_TYPE_DISPLAY_ORIENTATION:
> - CHECK(FUNC(sei_display_orientation)
> - (ctx, rw, ¤t->payload.display_orientation));
> - break;
> - case H264_SEI_TYPE_MASTERING_DISPLAY_COLOUR_VOLUME:
> - CHECK(FUNC_SEI(sei_mastering_display_colour_volume)
> - (ctx, rw,
> ¤t->payload.mastering_display_colour_volume));
> - break;
> - case H264_SEI_TYPE_ALTERNATIVE_TRANSFER:
> - CHECK(FUNC_SEI(sei_alternative_transfer_characteristics)
> - (ctx, rw,
> ¤t->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, ¤t->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, ¤t->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, ¤t->payload[k]));
> - }
> - }
> -#endif
> + CHECK(FUNC_SEI(message_list)(ctx, rw, ¤t->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, ¤t->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, ¤t->payload.name, \
> - ¤t->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, ¤t->payload.name, \
> - ¤t->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, ¤t->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, ¤t->payload.name, \
> - ¤t->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, ¤t->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, ¤t->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, ¤t->payload[k],
> prefix));
> - }
> - }
> -#endif
> + CHECK(FUNC_SEI(message_list)(ctx, rw, ¤t->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 = ¤t->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 = ¤t->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 at ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request at ffmpeg.org with subject "unsubscribe".
More information about the ffmpeg-devel
mailing list