From 3d51d3b42d0eee10301b06aaa814454c5e38eed6 Mon Sep 17 00:00:00 2001 From: James Almer Date: Fri, 24 Apr 2020 17:41:47 -0300 Subject: [PATCH] avcodec/cbs_h265: add missing support for reserved_payload_extension_data SEI bits Fixes ticket #8622 Reviewed-by: Andreas Rheinhardt Reviewed-by: Mark Thompson Signed-off-by: James Almer --- libavcodec/cbs_h2645.c | 1 + libavcodec/cbs_h265.h | 1 + libavcodec/cbs_h265_syntax_template.c | 85 +++++++++++++++++++++------ 3 files changed, 70 insertions(+), 17 deletions(-) diff --git a/libavcodec/cbs_h2645.c b/libavcodec/cbs_h2645.c index 095e449ddc..b432921ecc 100644 --- a/libavcodec/cbs_h2645.c +++ b/libavcodec/cbs_h2645.c @@ -553,6 +553,7 @@ static void cbs_h265_free_sei_payload(H265RawSEIPayload *payload) 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) diff --git a/libavcodec/cbs_h265.h b/libavcodec/cbs_h265.h index 2c1e153ad9..73897f77a4 100644 --- a/libavcodec/cbs_h265.h +++ b/libavcodec/cbs_h265.h @@ -715,6 +715,7 @@ typedef struct H265RawSEIPayload { AVBufferRef *data_ref; } other; } payload; + H265RawExtensionData extension_data; } H265RawSEIPayload; typedef struct H265RawSEI { diff --git a/libavcodec/cbs_h265_syntax_template.c b/libavcodec/cbs_h265_syntax_template.c index ed08b06e9c..d3ac618db6 100644 --- a/libavcodec/cbs_h265_syntax_template.c +++ b/libavcodec/cbs_h265_syntax_template.c @@ -1564,7 +1564,8 @@ static int FUNC(slice_segment_header)(CodedBitstreamContext *ctx, RWContext *rw, static int FUNC(sei_buffering_period)(CodedBitstreamContext *ctx, RWContext *rw, H265RawSEIBufferingPeriod *current, - uint32_t *payload_size) + uint32_t *payload_size, + int *more_data) { CodedBitstreamH265Context *h265 = ctx->priv_data; const H265RawSPS *sps; @@ -1658,8 +1659,15 @@ static int FUNC(sei_buffering_period)(CodedBitstreamContext *ctx, RWContext *rw, else infer(use_alt_cpb_params_flag, 0); #else - if (current->use_alt_cpb_params_flag) + // 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) { 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; + } #endif return 0; @@ -2057,11 +2065,48 @@ 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, end_position; + int start_position, current_position; + int more_data = !!current->extension_data.bit_length; #ifdef READ start_position = get_bits_count(rw); @@ -2093,8 +2138,15 @@ static int FUNC(sei_payload)(CodedBitstreamContext *ctx, RWContext *rw, 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 - SEI_TYPE_S(BUFFERING_PERIOD, 1, 0, buffering_period); + 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_S(USER_DATA_REGISTERED_ITU_T_T35, @@ -2125,24 +2177,23 @@ static int FUNC(sei_payload)(CodedBitstreamContext *ctx, RWContext *rw, } } - if (byte_alignment(rw)) { + // 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 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) >> 3; +#ifdef WRITE + current->payload_size = (put_bits_count(rw) - start_position) >> 3; #endif return 0;