1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2024-12-23 12:43:46 +02:00

cbs_h2645: Merge SEI message handling in common between codecs

This commit is contained in:
Mark Thompson 2021-01-01 21:35:13 +00:00
parent 773857df59
commit 8843607f49
12 changed files with 992 additions and 767 deletions

View File

@ -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

View File

@ -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 */

View File

@ -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
@ -1381,36 +1385,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 SEI_TYPE_BUFFERING_PERIOD:
case SEI_TYPE_PIC_TIMING:
case SEI_TYPE_PAN_SCAN_RECT:
case SEI_TYPE_RECOVERY_POINT:
case SEI_TYPE_DISPLAY_ORIENTATION:
case SEI_TYPE_MASTERING_DISPLAY_COLOUR_VOLUME:
case SEI_TYPE_ALTERNATIVE_TRANSFER_CHARACTERISTICS:
break;
case SEI_TYPE_USER_DATA_REGISTERED_ITU_T_T35:
av_buffer_unref(&payload->payload.user_data_registered.data_ref);
break;
case 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[] = {
@ -1442,42 +1421,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 SEI_TYPE_BUFFERING_PERIOD:
case SEI_TYPE_PIC_TIMING:
case SEI_TYPE_PAN_SCAN_RECT:
case SEI_TYPE_RECOVERY_POINT:
case SEI_TYPE_DISPLAY_ORIENTATION:
case SEI_TYPE_ACTIVE_PARAMETER_SETS:
case SEI_TYPE_DECODED_PICTURE_HASH:
case SEI_TYPE_TIME_CODE:
case SEI_TYPE_MASTERING_DISPLAY_COLOUR_VOLUME:
case SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO:
case SEI_TYPE_ALTERNATIVE_TRANSFER_CHARACTERISTICS:
case SEI_TYPE_ALPHA_CHANNEL_INFO:
break;
case SEI_TYPE_USER_DATA_REGISTERED_ITU_T_T35:
av_buffer_unref(&payload->payload.user_data_registered.data_ref);
break;
case 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[] = {
@ -1557,92 +1505,164 @@ const CodedBitstreamType ff_cbs_type_h265 = {
.close = &cbs_h265_close,
};
int ff_cbs_h264_add_sei_message(CodedBitstreamFragment *au,
H264RawSEIPayload *payload)
static const SEIMessageTypeDescriptor cbs_sei_common_types[] = {
{
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;
}
memcpy(&sei->payload[sei->payload_count], payload, sizeof(*payload));
++sei->payload_count;
return 0;
fail:
cbs_h264_free_sei_payload(payload);
return err;
}
void ff_cbs_h264_delete_sei_message(CodedBitstreamFragment *au,
CodedBitstreamUnit *nal,
int position)
SEI_TYPE_FILLER_PAYLOAD,
1, 1,
sizeof(SEIRawFillerPayload),
SEI_MESSAGE_RW(sei, filler_payload),
},
{
H264RawSEI *sei = nal->content;
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,
};
av_assert0(nal->type == H264_NAL_SEI);
av_assert0(position >= 0 && position < 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
};
if (position == 0 && sei->payload_count == 1) {
// Deleting NAL unit entirely.
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
};
const SEIMessageTypeDescriptor *ff_cbs_sei_find_type(CodedBitstreamContext *ctx,
int payload_type)
{
const SEIMessageTypeDescriptor *codec_list;
int i;
for (i = 0; i < au->nb_units; i++) {
if (&au->units[i] == nal)
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];
}
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;
}
ff_cbs_delete_unit(au, i);
} else {
cbs_h264_free_sei_payload(&sei->payload[position]);
for (i = 0; codec_list[i].type >= 0; i++) {
if (codec_list[i].type == payload_type)
return &codec_list[i];
}
--sei->payload_count;
memmove(sei->payload + position,
sei->payload + position + 1,
(sei->payload_count - position) * sizeof(*sei->payload));
}
return NULL;
}

View File

@ -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 SEI_TYPE_BUFFERING_PERIOD:
CHECK(FUNC(sei_buffering_period)
(ctx, rw, &current->payload.buffering_period));
break;
case SEI_TYPE_PIC_TIMING:
CHECK(FUNC(sei_pic_timing)
(ctx, rw, &current->payload.pic_timing));
break;
case SEI_TYPE_PAN_SCAN_RECT:
CHECK(FUNC(sei_pan_scan_rect)
(ctx, rw, &current->payload.pan_scan_rect));
break;
case SEI_TYPE_FILLER_PAYLOAD:
{
for (i = 0; i < current->payload_size; i++)
fixed(8, ff_byte, 0xff);
}
break;
case SEI_TYPE_USER_DATA_REGISTERED_ITU_T_T35:
CHECK(FUNC_SEI(sei_user_data_registered)
(ctx, rw, &current->payload.user_data_registered, &current->payload_size));
break;
case SEI_TYPE_USER_DATA_UNREGISTERED:
CHECK(FUNC_SEI(sei_user_data_unregistered)
(ctx, rw, &current->payload.user_data_unregistered, &current->payload_size));
break;
case SEI_TYPE_RECOVERY_POINT:
CHECK(FUNC(sei_recovery_point)
(ctx, rw, &current->payload.recovery_point));
break;
case SEI_TYPE_DISPLAY_ORIENTATION:
CHECK(FUNC(sei_display_orientation)
(ctx, rw, &current->payload.display_orientation));
break;
case SEI_TYPE_MASTERING_DISPLAY_COLOUR_VOLUME:
CHECK(FUNC_SEI(sei_mastering_display_colour_volume)
(ctx, rw, &current->payload.mastering_display_colour_volume));
break;
case SEI_TYPE_ALTERNATIVE_TRANSFER_CHARACTERISTICS:
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));

View File

@ -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 {

View File

@ -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,159 +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 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 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 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 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 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(PIC_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_COLOUR_VOLUME,
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_N
#undef SEI_TYPE_S
#undef SEI_TYPE_E
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");
@ -2149,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));

369
libavcodec/cbs_sei.c Normal file
View File

@ -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) {
av_freep(&message->payload);
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 - 1; 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 = list->nb_messages - 1; j >= 0; j--) {
if (list->messages[j].payload_type == payload_type)
cbs_sei_delete_message(list, j);
}
}
}

View File

@ -21,8 +21,16 @@
#include <stddef.h>
#include <stdint.h>
#include "libavutil/buffer.h"
#include "cbs.h"
#include "sei.h"
typedef struct SEIRawFillerPayload {
uint32_t payload_size;
} SEIRawFillerPayload;
typedef struct SEIRawUserDataRegistered {
uint8_t itu_t_t35_country_code;
@ -57,4 +65,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 */

View File

@ -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;
}

View File

@ -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,
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,42 +431,20 @@ 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 ==
SEI_TYPE_FILLER_PAYLOAD)
ff_cbs_h264_delete_sei_message(au, &au->units[i], j);
}
}
}
ff_cbs_sei_delete_message_type(ctx->output, au,
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;
SEIRawMessage *message = NULL;
while (ff_cbs_sei_find_message(ctx->output, au,
SEI_TYPE_DISPLAY_ORIENTATION,
&message) == 0) {
H264RawSEIDisplayOrientation *disp = message->payload;
int32_t *matrix;
if (sei->payload[j].payload_type !=
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);
@ -488,14 +469,16 @@ static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *pkt)
goto fail;
}
}
if (ctx->display_orientation == REMOVE ||
ctx->display_orientation == INSERT) {
ff_cbs_sei_delete_message_type(ctx->output, au,
SEI_TYPE_DISPLAY_ORIENTATION);
}
}
if (ctx->display_orientation == INSERT) {
H264RawSEIPayload payload = {
.payload_type = 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,
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 =
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++) {

View File

@ -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 = 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,
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 = SEI_TYPE_BUFFERING_PERIOD;
sei->payload[i].payload.buffering_period = priv->sei_buffering_period;
++i;
}
sei->payload[i].payload_type = SEI_TYPE_PIC_TIMING;
sei->payload[i].payload.pic_timing = priv->sei_pic_timing;
++i;
}
if (priv->sei_needed & SEI_RECOVERY_POINT) {
sei->payload[i].payload_type = SEI_TYPE_RECOVERY_POINT;
sei->payload[i].payload.recovery_point = priv->sei_recovery_point;
++i;
}
sei->payload_count = i;
av_assert0(sei->payload_count > 0);
err = vaapi_encode_h264_add_nal(avctx, au, sei);
err = ff_cbs_sei_add_message(priv->cbc, au, 1,
SEI_TYPE_BUFFERING_PERIOD,
&priv->sei_buffering_period, NULL);
if (err < 0)
goto fail;
}
err = ff_cbs_sei_add_message(priv->cbc, au, 1,
SEI_TYPE_PIC_TIMING,
&priv->sei_pic_timing, NULL);
if (err < 0)
goto fail;
}
if (priv->sei_needed & SEI_RECOVERY_POINT) {
err = ff_cbs_sei_add_message(priv->cbc, au, 1,
SEI_TYPE_RECOVERY_POINT,
&priv->sei_recovery_point, NULL);
if (err < 0)
goto fail;
}
priv->sei_needed = 0;
err = vaapi_encode_h264_write_access_unit(avctx, data, data_len, au);

View File

@ -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 = SEI_TYPE_MASTERING_DISPLAY_COLOUR_VOLUME;
sei->payload[i].payload.mastering_display_colour_volume =
priv->sei_mastering_display;
++i;
err = ff_cbs_sei_add_message(priv->cbc, au, 1,
SEI_TYPE_MASTERING_DISPLAY_COLOUR_VOLUME,
&priv->sei_mastering_display, NULL);
if (err < 0)
goto fail;
}
if (priv->sei_needed & SEI_CONTENT_LIGHT_LEVEL) {
sei->payload[i].payload_type = SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO;
sei->payload[i].payload.content_light_level = priv->sei_content_light_level;
++i;
}
sei->payload_count = i;
av_assert0(sei->payload_count > 0);
err = vaapi_encode_h265_add_nal(avctx, au, sei);
err = ff_cbs_sei_add_message(priv->cbc, au, 1,
SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO,
&priv->sei_content_light_level, NULL);
if (err < 0)
goto fail;
}
priv->sei_needed = 0;
err = vaapi_encode_h265_write_access_unit(avctx, data, data_len, au);