From patchwork Sat Oct 27 21:39:49 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Thompson X-Patchwork-Id: 10806 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 5F7DB44D8FD for ; Sun, 28 Oct 2018 00:40:04 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id CC30A68A31C; Sun, 28 Oct 2018 00:39:35 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wr1-f67.google.com (mail-wr1-f67.google.com [209.85.221.67]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id DAAC668A276 for ; Sun, 28 Oct 2018 00:39:27 +0300 (EEST) Received: by mail-wr1-f67.google.com with SMTP id d10-v6so4674748wrs.5 for ; Sat, 27 Oct 2018 14:39:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=jkqxz-net.20150623.gappssmtp.com; s=20150623; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=1D6hDYREGjaRLZ4Bh5Zev0HEb/GD2SqOo2ooooJWbKk=; b=xUJegA0qzkCyPiS0RF+TzT3928pEEBjGZkTvuL7x0JI1bip4nIPG6d9WoA8F76gpoS N/d2moV62hxWPCaXvaytOy+hWcQokOIIgT3RuOXz2eaoLAfkU5kbTJuaHUwBI4NwOR3s e0StRChwb1zC7Z5ohWRAFevMKmB4IvrjkjJL3FCk5W2b5UcMz+Yya9KCdoHqIJiDMD/q sbzsmbvylHBXFBH8SPNnTFZldp0lw2NiGzqPatdr+COfOWsJUy3j3mlNi1uxQS+36K1I T8qHn9F17k1vcOqLZlBcEmATVsRIVH96IflLSbHgx57omS0m/AeyFhpoOTbYH0uPmfr7 dZcw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=1D6hDYREGjaRLZ4Bh5Zev0HEb/GD2SqOo2ooooJWbKk=; b=K2085VEYiDgYPN50gFRFl2nzAQcStn+YmWS43hN69A1oFEyox6oM4hraMvgKyQZtKC CNvdegBqPtHU72//JHYHpRqU878qpbsrSHpGcS0NiBoN849XNcUDOR0EnUYH+YCeu+nm 5xdP5OXy+sNOZVoDl/sWU7l8mABlZt0A5g2vFNmR1SFutnb7YRi4j75XJuJ4SCgLSLxQ yOfl8R5cDISnoxNgNFT80ycERkb9Q0iTmi0KHkM67i5m1C67o3Sxp0Xmn5LTMvLRlPV0 YCRYNf42oivF3GVE/LawrnfWvVYIHJVb7XarWsEo2avFakAPRzdOcAMvt0H7y5nnvhrW 9E6Q== X-Gm-Message-State: AGRZ1gKYyvcWrp7xhnHtSyQ6+u6UKAeUCXhSYMgNpw2VOhSTZoXF6uP6 nCKfM3lyQBQncklwgJWVnSSW/zq31HQ= X-Google-Smtp-Source: AJdET5di2rj7+Kr/AgSOPdRp3yI/toeI4ZPNQaTsA8ZAp2j8Kii/ud2b/aEQhPRvx1qXuCqzYORdPw== X-Received: by 2002:adf:fbc9:: with SMTP id d9-v6mr10038585wrs.27.1540676396890; Sat, 27 Oct 2018 14:39:56 -0700 (PDT) Received: from rywe.jkqxz.net (cpc91242-cmbg18-2-0-cust650.5-4.cable.virginm.net. [82.8.130.139]) by smtp.gmail.com with ESMTPSA id x18-v6sm7604798wme.42.2018.10.27.14.39.55 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sat, 27 Oct 2018 14:39:55 -0700 (PDT) From: Mark Thompson To: ffmpeg-devel@ffmpeg.org Date: Sat, 27 Oct 2018 22:39:49 +0100 Message-Id: <20181027213950.27048-4-sw@jkqxz.net> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20181027213950.27048-1-sw@jkqxz.net> References: <20181027213950.27048-1-sw@jkqxz.net> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 4/4] cbs_h265: Add a lot more SEI parsing support X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Supports both prefix and suffix SEI, decoding all of the common SEI types and some more obscure ones. Most of this is tested by the existing tests in fate. --- libavcodec/cbs_h2645.c | 20 +- libavcodec/cbs_h265.h | 124 +++++++ libavcodec/cbs_h265_syntax_template.c | 497 +++++++++++++++++++++++++- libavcodec/hevc.h | 3 + libavcodec/hevc_sei.h | 1 + 5 files changed, 625 insertions(+), 20 deletions(-) diff --git a/libavcodec/cbs_h2645.c b/libavcodec/cbs_h2645.c index e55bd00183..86baf129ce 100644 --- a/libavcodec/cbs_h2645.c +++ b/libavcodec/cbs_h2645.c @@ -490,9 +490,21 @@ static void cbs_h265_free_slice(void *unit, uint8_t *content) 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_DECODED_PICTURE_HASH: case HEVC_SEI_TYPE_MASTERING_DISPLAY_INFO: case HEVC_SEI_TYPE_CONTENT_LIGHT_LEVEL_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; @@ -1029,6 +1041,7 @@ static int cbs_h265_read_nal_unit(CodedBitstreamContext *ctx, break; case HEVC_NAL_SEI_PREFIX: + case HEVC_NAL_SEI_SUFFIX: { err = ff_cbs_alloc_unit_content(ctx, unit, sizeof(H265RawSEI), &cbs_h265_free_sei); @@ -1036,7 +1049,8 @@ static int cbs_h265_read_nal_unit(CodedBitstreamContext *ctx, if (err < 0) return err; - err = cbs_h265_read_sei(ctx, &gbc, unit->content); + err = cbs_h265_read_sei(ctx, &gbc, unit->content, + unit->type == HEVC_NAL_SEI_PREFIX); if (err < 0) return err; @@ -1300,8 +1314,10 @@ static int cbs_h265_write_nal_unit(CodedBitstreamContext *ctx, break; case HEVC_NAL_SEI_PREFIX: + case HEVC_NAL_SEI_SUFFIX: { - err = cbs_h265_write_sei(ctx, pbc, unit->content); + err = cbs_h265_write_sei(ctx, pbc, unit->content, + unit->type == HEVC_NAL_SEI_PREFIX); if (err < 0) return err; diff --git a/libavcodec/cbs_h265.h b/libavcodec/cbs_h265.h index 97c9444cb4..2bd3c31e30 100644 --- a/libavcodec/cbs_h265.h +++ b/libavcodec/cbs_h265.h @@ -548,6 +548,120 @@ typedef struct H265RawSlice { AVBufferRef *data_ref; } H265RawSlice; + +typedef struct H265RawSEIBufferingPeriod { + uint8_t bp_seq_parameter_set_id; + uint8_t irap_cpb_params_present_flag; + uint32_t cpb_delay_offset; + uint32_t dpb_delay_offset; + uint8_t concatenation_flag; + uint32_t au_cpb_removal_delay_delta_minus1; + + uint32_t nal_initial_cpb_removal_delay[HEVC_MAX_CPB_CNT]; + uint32_t nal_initial_cpb_removal_offset[HEVC_MAX_CPB_CNT]; + uint32_t nal_initial_alt_cpb_removal_delay[HEVC_MAX_CPB_CNT]; + uint32_t nal_initial_alt_cpb_removal_offset[HEVC_MAX_CPB_CNT]; + + uint32_t vcl_initial_cpb_removal_delay[HEVC_MAX_CPB_CNT]; + uint32_t vcl_initial_cpb_removal_offset[HEVC_MAX_CPB_CNT]; + uint32_t vcl_initial_alt_cpb_removal_delay[HEVC_MAX_CPB_CNT]; + uint32_t vcl_initial_alt_cpb_removal_offset[HEVC_MAX_CPB_CNT]; + + uint8_t use_alt_cpb_params_flag; +} H265RawSEIBufferingPeriod; + +typedef struct H265RawSEIPicTiming { + uint8_t pic_struct; + uint8_t source_scan_type; + uint8_t duplicate_flag; + + uint32_t au_cpb_removal_delay_minus1; + uint32_t pic_dpb_output_delay; + uint32_t pic_dpb_output_du_delay; + + uint16_t num_decoding_units_minus1; + uint8_t du_common_cpb_removal_delay_flag; + uint32_t du_common_cpb_removal_delay_increment_minus1; + uint16_t num_nalus_in_du_minus1[HEVC_MAX_SLICE_SEGMENTS]; + uint32_t du_cpb_removal_delay_increment_minus1[HEVC_MAX_SLICE_SEGMENTS]; +} H265RawSEIPicTiming; + +typedef struct H265RawSEIPanScanRect { + uint32_t pan_scan_rect_id; + uint8_t pan_scan_rect_cancel_flag; + uint8_t pan_scan_cnt_minus1; + int32_t pan_scan_rect_left_offset[3]; + int32_t pan_scan_rect_right_offset[3]; + int32_t pan_scan_rect_top_offset[3]; + int32_t pan_scan_rect_bottom_offset[3]; + uint16_t pan_scan_rect_persistence_flag; +} H265RawSEIPanScanRect; + +typedef struct H265RawSEIUserDataRegistered { + uint8_t itu_t_t35_country_code; + uint8_t itu_t_t35_country_code_extension_byte; + uint8_t *data; + size_t data_length; + AVBufferRef *data_ref; +} H265RawSEIUserDataRegistered; + +typedef struct H265RawSEIUserDataUnregistered { + uint8_t uuid_iso_iec_11578[16]; + uint8_t *data; + size_t data_length; + AVBufferRef *data_ref; +} H265RawSEIUserDataUnregistered; + +typedef struct H265RawSEIRecoveryPoint { + int16_t recovery_poc_cnt; + uint8_t exact_match_flag; + uint8_t broken_link_flag; +} H265RawSEIRecoveryPoint; + +typedef struct H265RawSEIDisplayOrientation { + uint8_t display_orientation_cancel_flag; + uint8_t hor_flip; + uint8_t ver_flip; + uint16_t anticlockwise_rotation; + uint16_t display_orientation_repetition_period; + uint8_t display_orientation_persistence_flag; +} H265RawSEIDisplayOrientation; + +typedef struct H265RawSEIActiveParameterSets { + uint8_t active_video_parameter_set_id; + uint8_t self_contained_cvs_flag; + uint8_t no_parameter_set_update_flag; + uint8_t num_sps_ids_minus1; + uint8_t active_seq_parameter_set_id[HEVC_MAX_SPS_COUNT]; + uint8_t layer_sps_idx[HEVC_MAX_LAYERS]; +} H265RawSEIActiveParameterSets; + +typedef struct H265RawSEIDecodedPictureHash { + uint8_t hash_type; + uint8_t picture_md5[3][16]; + uint16_t picture_crc[3]; + uint32_t picture_checksum[3]; +} H265RawSEIDecodedPictureHash; + +typedef struct H265RawSEITimeCode { + uint8_t num_clock_ts; + uint8_t clock_timestamp_flag[3]; + uint8_t units_field_based_flag[3]; + uint8_t counting_type[3]; + uint8_t full_timestamp_flag[3]; + uint8_t discontinuity_flag[3]; + uint8_t cnt_dropped_flag[3]; + uint16_t n_frames[3]; + uint8_t seconds_value[3]; + uint8_t minutes_value[3]; + uint8_t hours_value[3]; + uint8_t seconds_flag[3]; + uint8_t minutes_flag[3]; + uint8_t hours_flag[3]; + uint8_t time_offset_length[3]; + uint32_t time_offset_value[3]; +} H265RawSEITimeCode; + typedef struct H265RawSEIMasteringDisplayColourVolume { uint16_t display_primaries_x[3]; uint16_t display_primaries_y[3]; @@ -566,6 +680,16 @@ typedef struct H265RawSEIPayload { uint32_t payload_type; uint32_t payload_size; union { + H265RawSEIBufferingPeriod buffering_period; + H265RawSEIPicTiming pic_timing; + H265RawSEIPanScanRect pan_scan_rect; + H265RawSEIUserDataRegistered user_data_registered; + H265RawSEIUserDataUnregistered user_data_unregistered; + H265RawSEIRecoveryPoint recovery_point; + H265RawSEIDisplayOrientation display_orientation; + H265RawSEIActiveParameterSets active_parameter_sets; + H265RawSEIDecodedPictureHash decoded_picture_hash; + H265RawSEITimeCode time_code; H265RawSEIMasteringDisplayColourVolume mastering_display; H265RawSEIContentLightLevelInfo content_light_level; struct { diff --git a/libavcodec/cbs_h265_syntax_template.c b/libavcodec/cbs_h265_syntax_template.c index b8a9bab971..e73460db80 100644 --- a/libavcodec/cbs_h265_syntax_template.c +++ b/libavcodec/cbs_h265_syntax_template.c @@ -1564,11 +1564,439 @@ 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) +{ + CodedBitstreamH265Context *h265 = ctx->priv_data; + const H265RawSPS *sps; + const H265RawHRDParameters *hrd; + int err, i, length; + +#ifdef READ + int start_pos, end_pos, bits_left; + start_pos = get_bits_count(rw); +#endif + + HEADER("Buffering Period"); + + ue(bp_seq_parameter_set_id, 0, HEVC_MAX_SPS_COUNT - 1); + + sps = h265->sps[current->bp_seq_parameter_set_id]; + if (!sps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "SPS id %d not available.\n", + current->bp_seq_parameter_set_id); + return AVERROR_INVALIDDATA; + } + h265->active_sps = sps; + + if (!sps->vui_parameters_present_flag || + !sps->vui.vui_hrd_parameters_present_flag) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Buffering period SEI requires " + "HRD parameters to be present in SPS.\n"); + return AVERROR_INVALIDDATA; + } + hrd = &sps->vui.hrd_parameters; + if (!hrd->nal_hrd_parameters_present_flag && + !hrd->vcl_hrd_parameters_present_flag) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Buffering period SEI requires " + "NAL or VCL HRD parameters to be present.\n"); + return AVERROR_INVALIDDATA; + } + + if (!hrd->sub_pic_hrd_params_present_flag) + flag(irap_cpb_params_present_flag); + else + infer(irap_cpb_params_present_flag, 0); + if (current->irap_cpb_params_present_flag) { + length = hrd->au_cpb_removal_delay_length_minus1 + 1; + u(length, cpb_delay_offset, 0, MAX_UINT_BITS(length)); + length = hrd->dpb_output_delay_length_minus1 + 1; + u(length, dpb_delay_offset, 0, MAX_UINT_BITS(length)); + } else { + infer(cpb_delay_offset, 0); + infer(dpb_delay_offset, 0); + } + + flag(concatenation_flag); + + length = hrd->au_cpb_removal_delay_length_minus1 + 1; + u(length, au_cpb_removal_delay_delta_minus1, 0, MAX_UINT_BITS(length)); + + if (hrd->nal_hrd_parameters_present_flag) { + for (i = 0; i <= hrd->cpb_cnt_minus1[0]; i++) { + length = hrd->initial_cpb_removal_delay_length_minus1 + 1; + + us(length, nal_initial_cpb_removal_delay[i], + 0, MAX_UINT_BITS(length), 1, i); + us(length, nal_initial_cpb_removal_offset[i], + 0, MAX_UINT_BITS(length), 1, i); + + if (hrd->sub_pic_hrd_params_present_flag || + current->irap_cpb_params_present_flag) { + us(length, nal_initial_alt_cpb_removal_delay[i], + 0, MAX_UINT_BITS(length), 1, i); + us(length, nal_initial_alt_cpb_removal_offset[i], + 0, MAX_UINT_BITS(length), 1, i); + } + } + } + if (hrd->vcl_hrd_parameters_present_flag) { + for (i = 0; i <= hrd->cpb_cnt_minus1[0]; i++) { + length = hrd->initial_cpb_removal_delay_length_minus1 + 1; + + us(length, vcl_initial_cpb_removal_delay[i], + 0, MAX_UINT_BITS(length), 1, i); + us(length, vcl_initial_cpb_removal_offset[i], + 0, MAX_UINT_BITS(length), 1, i); + + if (hrd->sub_pic_hrd_params_present_flag || + current->irap_cpb_params_present_flag) { + us(length, vcl_initial_alt_cpb_removal_delay[i], + 0, MAX_UINT_BITS(length), 1, i); + us(length, vcl_initial_alt_cpb_removal_offset[i], + 0, MAX_UINT_BITS(length), 1, i); + } + } + } + +#ifdef READ + // payload_extension_present() - true if we are before the last 1-bit + // in the payload structure, which must be in the last byte. + end_pos = get_bits_count(rw); + bits_left = *payload_size * 8 - (end_pos - start_pos); + if (bits_left > 0 && + (bits_left > 7 || ff_ctz(show_bits(rw, bits_left)) < bits_left - 1)) + flag(use_alt_cpb_params_flag); + else + infer(use_alt_cpb_params_flag, 0); +#else + if (current->use_alt_cpb_params_flag) + flag(use_alt_cpb_params_flag); +#endif + + return 0; +} + +static int FUNC(sei_pic_timing)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSEIPicTiming *current) +{ + CodedBitstreamH265Context *h265 = ctx->priv_data; + const H265RawSPS *sps; + const H265RawHRDParameters *hrd; + int err, expected_source_scan_type, i, length; + + HEADER("Picture Timing"); + + sps = h265->active_sps; + if (!sps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "No active SPS for pic_timing.\n"); + return AVERROR_INVALIDDATA; + } + + expected_source_scan_type = 2 - + 2 * sps->profile_tier_level.general_interlaced_source_flag - + sps->profile_tier_level.general_progressive_source_flag; + + if (sps->vui.frame_field_info_present_flag) { + u(4, pic_struct, 0, 12); + u(2, source_scan_type, + expected_source_scan_type >= 0 ? expected_source_scan_type : 0, + expected_source_scan_type >= 0 ? expected_source_scan_type : 2); + flag(duplicate_flag); + } else { + infer(pic_struct, 0); + infer(source_scan_type, + expected_source_scan_type >= 0 ? expected_source_scan_type : 2); + infer(duplicate_flag, 0); + } + + if (sps->vui_parameters_present_flag && + sps->vui.vui_hrd_parameters_present_flag) + hrd = &sps->vui.hrd_parameters; + else + hrd = NULL; + if (hrd && (hrd->nal_hrd_parameters_present_flag || + hrd->vcl_hrd_parameters_present_flag)) { + length = hrd->au_cpb_removal_delay_length_minus1 + 1; + u(length, au_cpb_removal_delay_minus1, 0, MAX_UINT_BITS(length)); + + length = hrd->dpb_output_delay_length_minus1 + 1; + u(length, pic_dpb_output_delay, 0, MAX_UINT_BITS(length)); + + if (hrd->sub_pic_hrd_params_present_flag) { + length = hrd->dpb_output_delay_du_length_minus1 + 1; + u(length, pic_dpb_output_du_delay, 0, MAX_UINT_BITS(length)); + } + + if (hrd->sub_pic_hrd_params_present_flag && + hrd->sub_pic_cpb_params_in_pic_timing_sei_flag) { + // Each decoding unit must contain at least one slice segment. + ue(num_decoding_units_minus1, 0, HEVC_MAX_SLICE_SEGMENTS); + flag(du_common_cpb_removal_delay_flag); + + length = hrd->du_cpb_removal_delay_increment_length_minus1 + 1; + if (current->du_common_cpb_removal_delay_flag) + u(length, du_common_cpb_removal_delay_increment_minus1, + 0, MAX_UINT_BITS(length)); + + for (i = 0; i <= current->num_decoding_units_minus1; i++) { + ues(num_nalus_in_du_minus1[i], + 0, HEVC_MAX_SLICE_SEGMENTS, 1, i); + if (!current->du_common_cpb_removal_delay_flag && + i < current->num_decoding_units_minus1) + us(length, du_cpb_removal_delay_increment_minus1[i], + 0, MAX_UINT_BITS(length), 1, i); + } + } + } + + return 0; +} + +static int FUNC(sei_pan_scan_rect)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSEIPanScanRect *current) +{ + int err, i; + + HEADER("Pan-Scan Rectangle"); + + ue(pan_scan_rect_id, 0, UINT32_MAX - 1); + flag(pan_scan_rect_cancel_flag); + + if (!current->pan_scan_rect_cancel_flag) { + ue(pan_scan_cnt_minus1, 0, 2); + + for (i = 0; i <= current->pan_scan_cnt_minus1; i++) { + ses(pan_scan_rect_left_offset[i], INT32_MIN + 1, INT32_MAX, 1, i); + ses(pan_scan_rect_right_offset[i], INT32_MIN + 1, INT32_MAX, 1, i); + ses(pan_scan_rect_top_offset[i], INT32_MIN + 1, INT32_MAX, 1, i); + ses(pan_scan_rect_bottom_offset[i], INT32_MIN + 1, INT32_MAX, 1, i); + } + + flag(pan_scan_rect_persistence_flag); + } + + return 0; +} + +static int FUNC(sei_user_data_registered)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSEIUserDataRegistered *current, + uint32_t *payload_size) +{ + int err, i, j; + + HEADER("User Data Registered ITU-T T.35"); + + u(8, itu_t_t35_country_code, 0x00, 0xff); + if (current->itu_t_t35_country_code != 0xff) + i = 1; + else { + u(8, itu_t_t35_country_code_extension_byte, 0x00, 0xff); + i = 2; + } + +#ifdef READ + if (*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; +#endif + + allocate(current->data, current->data_length); + for (j = 0; j < current->data_length; j++) + xu(8, itu_t_t35_payload_byte[i], current->data[j], 0x00, 0xff, 1, i + j); + + return 0; +} + +static int FUNC(sei_user_data_unregistered)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSEIUserDataUnregistered *current, + uint32_t *payload_size) +{ + int err, i; + + HEADER("User Data Unregistered"); + +#ifdef READ + if (*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; +#endif + + for (i = 0; i < 16; i++) + us(8, uuid_iso_iec_11578[i], 0x00, 0xff, 1, i); + + allocate(current->data, current->data_length); + + for (i = 0; i < current->data_length; i++) + xu(8, user_data_payload_byte[i], current->data[i], 0x00, 0xff, 1, i); + + return 0; +} + +static int FUNC(sei_recovery_point)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSEIRecoveryPoint *current) +{ + int err; + + HEADER("Recovery Point"); + + se(recovery_poc_cnt, -32768, 32767); + + flag(exact_match_flag); + flag(broken_link_flag); + + return 0; +} + +static int FUNC(sei_display_orientation)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSEIDisplayOrientation *current) +{ + int err; + + HEADER("Display Orientation"); + + flag(display_orientation_cancel_flag); + if (!current->display_orientation_cancel_flag) { + flag(hor_flip); + flag(ver_flip); + u(16, anticlockwise_rotation, 0, 65535); + flag(display_orientation_persistence_flag); + } + + return 0; +} + +static int FUNC(sei_active_parameter_sets)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSEIActiveParameterSets *current) +{ + CodedBitstreamH265Context *h265 = ctx->priv_data; + const H265RawVPS *vps; + int err, i; + + HEADER("Active Parameter Sets"); + + u(4, active_video_parameter_set_id, 0, HEVC_MAX_VPS_COUNT); + vps = h265->vps[current->active_video_parameter_set_id]; + if (!vps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "VPS id %d not available for active " + "parameter sets.\n", current->active_video_parameter_set_id); + return AVERROR_INVALIDDATA; + } + h265->active_vps = vps; + + flag(self_contained_cvs_flag); + flag(no_parameter_set_update_flag); + + ue(num_sps_ids_minus1, 0, HEVC_MAX_SPS_COUNT - 1); + for (i = 0; i <= current->num_sps_ids_minus1; i++) + ues(active_seq_parameter_set_id[i], 0, HEVC_MAX_SPS_COUNT - 1, 1, i); + + for (i = vps->vps_base_layer_internal_flag; + i <= FFMIN(62, vps->vps_max_layers_minus1); i++) { + ues(layer_sps_idx[i], 0, current->num_sps_ids_minus1, 1, i); + + if (i == 0) + h265->active_sps = h265->sps[current->active_seq_parameter_set_id[current->layer_sps_idx[0]]]; + } + + return 0; +} + +static int FUNC(sei_decoded_picture_hash)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSEIDecodedPictureHash *current) +{ + CodedBitstreamH265Context *h265 = ctx->priv_data; + const H265RawSPS *sps = h265->active_sps; + int err, c, i; + + HEADER("Decoded Picture Hash"); + + if (!sps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "No active SPS for decoded picture hash.\n"); + return AVERROR_INVALIDDATA; + } + + u(8, hash_type, 0, 2); + + for (c = 0; c < (sps->chroma_format_idc == 0 ? 1 : 3); c++) { + if (current->hash_type == 0) { + for (i = 0; i < 16; i++) + us(8, picture_md5[c][i], 0x00, 0xff, 2, c, i); + } else if (current->hash_type == 1) { + us(16, picture_crc[c], 0x0000, 0xffff, 1, c); + } else if (current->hash_type == 2) { + us(32, picture_checksum[c], 0x00000000, 0xffffffff, 1, c); + } + } + + return 0; +} + +static int FUNC(sei_time_code)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSEITimeCode *current) +{ + int err, i; + + HEADER("Time Code"); + + u(2, num_clock_ts, 1, 3); + + for (i = 0; i < current->num_clock_ts; i++) { + flags(units_field_based_flag[i], 1, i); + us(5, counting_type[i], 0, 6, 1, i); + flags(full_timestamp_flag[i], 1, i); + flags(discontinuity_flag[i], 1, i); + flags(cnt_dropped_flag[i], 1, i); + + us(9, n_frames[i], 0, MAX_UINT_BITS(9), 1, i); + + if (current->full_timestamp_flag[i]) { + us(6, seconds_value[i], 0, 59, 1, i); + us(6, minutes_value[i], 0, 59, 1, i); + us(5, hours_value[i], 0, 23, 1, i); + } else { + flags(seconds_flag[i], 1, i); + if (current->seconds_flag[i]) { + us(6, seconds_value[i], 0, 59, 1, i); + flags(minutes_flag[i], 1, i); + if (current->minutes_flag[i]) { + us(6, minutes_value[i], 0, 59, 1, i); + flags(hours_flag[i], 1, i); + if (current->hours_flag[i]) + us(5, hours_value[i], 0, 23, 1, i); + } + } + } + + us(5, time_offset_length[i], 0, 31, 1, i); + if (current->time_offset_length[i] > 0) + us(current->time_offset_length[i], time_offset_value[i], + 0, MAX_UINT_BITS(current->time_offset_length[i]), 1, i); + } + + return 0; +} + static int FUNC(sei_mastering_display)(CodedBitstreamContext *ctx, RWContext *rw, H265RawSEIMasteringDisplayColourVolume *current) { int err, c; + HEADER("Mastering Display Colour Volume"); + for (c = 0; c < 3; c++) { us(16, display_primaries_x[c], 0, 50000, 1, c); us(16, display_primaries_y[c], 0, 50000, 1, c); @@ -1590,6 +2018,8 @@ static int FUNC(sei_content_light_level)(CodedBitstreamContext *ctx, RWContext * { int err; + HEADER("Content Light Level"); + u(16, max_content_light_level, 0, MAX_UINT_BITS(16)); u(16, max_pic_average_light_level, 0, MAX_UINT_BITS(16)); @@ -1597,7 +2027,7 @@ static int FUNC(sei_content_light_level)(CodedBitstreamContext *ctx, RWContext * } static int FUNC(sei_payload)(CodedBitstreamContext *ctx, RWContext *rw, - H265RawSEIPayload *current) + H265RawSEIPayload *current, int prefix) { int err, i; int start_position, end_position; @@ -1609,18 +2039,45 @@ static int FUNC(sei_payload)(CodedBitstreamContext *ctx, RWContext *rw, #endif switch (current->payload_type) { - case HEVC_SEI_TYPE_MASTERING_DISPLAY_INFO: - CHECK(FUNC(sei_mastering_display) - (ctx, rw, ¤t->payload.mastering_display)); - - break; - - case HEVC_SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO: - CHECK(FUNC(sei_content_light_level) - (ctx, rw, ¤t->payload.content_light_level)); - - break; - +#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 + + SEI_TYPE_S(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, + 1, 1, user_data_registered); + SEI_TYPE_S(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_N(MASTERING_DISPLAY_INFO, 1, 0, mastering_display); + SEI_TYPE_N(CONTENT_LIGHT_LEVEL_INFO, 1, 0, content_light_level); + +#undef SEI_TYPE default: { #ifdef READ @@ -1658,14 +2115,18 @@ static int FUNC(sei_payload)(CodedBitstreamContext *ctx, RWContext *rw, } static int FUNC(sei)(CodedBitstreamContext *ctx, RWContext *rw, - H265RawSEI *current) + H265RawSEI *current, int prefix) { int err, k; - HEADER("Supplemental Enhancement Information"); + if (prefix) + HEADER("Prefix Supplemental Enhancement Information"); + else + HEADER("Suffix Supplemental Enhancement Information"); CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header, - HEVC_NAL_SEI_PREFIX)); + prefix ? HEVC_NAL_SEI_PREFIX + : HEVC_NAL_SEI_SUFFIX)); #ifdef READ for (k = 0; k < H265_MAX_SEI_PAYLOADS; k++) { @@ -1690,7 +2151,7 @@ static int FUNC(sei)(CodedBitstreamContext *ctx, RWContext *rw, current->payload[k].payload_type = payload_type; current->payload[k].payload_size = payload_size; - CHECK(FUNC(sei_payload)(ctx, rw, ¤t->payload[k])); + CHECK(FUNC(sei_payload)(ctx, rw, ¤t->payload[k], prefix)); if (!cbs_h2645_read_more_rbsp_data(rw)) break; @@ -1729,7 +2190,7 @@ static int FUNC(sei)(CodedBitstreamContext *ctx, RWContext *rw, } xu(8, last_payload_size_byte, tmp, 0, 254, 0); - CHECK(FUNC(sei_payload)(ctx, rw, ¤t->payload[k])); + CHECK(FUNC(sei_payload)(ctx, rw, ¤t->payload[k], prefix)); } } #endif diff --git a/libavcodec/hevc.h b/libavcodec/hevc.h index 670168eb52..56b5541d90 100644 --- a/libavcodec/hevc.h +++ b/libavcodec/hevc.h @@ -143,6 +143,9 @@ enum { // A.4.1: table A.6 allows at most 20 tile columns for any level. HEVC_MAX_TILE_COLUMNS = 20, + // A.4.2: table A.6 allows at most 600 slice segments for any level. + HEVC_MAX_SLICE_SEGMENTS = 600, + // 7.4.7.1: in the worst case (tiles_enabled_flag and // entropy_coding_sync_enabled_flag are both set), entry points can be // placed at the beginning of every Ctb row in every tile, giving an diff --git a/libavcodec/hevc_sei.h b/libavcodec/hevc_sei.h index e92da25bbf..2fec00ace0 100644 --- a/libavcodec/hevc_sei.h +++ b/libavcodec/hevc_sei.h @@ -52,6 +52,7 @@ typedef enum { HEVC_SEI_TYPE_DECODED_PICTURE_HASH = 132, HEVC_SEI_TYPE_SCALABLE_NESTING = 133, HEVC_SEI_TYPE_REGION_REFRESH_INFO = 134, + HEVC_SEI_TYPE_TIME_CODE = 136, HEVC_SEI_TYPE_MASTERING_DISPLAY_INFO = 137, HEVC_SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO = 144, HEVC_SEI_TYPE_ALTERNATIVE_TRANSFER_CHARACTERISTICS = 147,