| /* |
| * VVC Supplementary Enhancement Information messages |
| * |
| * copyright (c) 2024 Wu Jianhua <toqsxw@outlook.com> |
| * |
| * 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 "sei.h" |
| #include "dec.h" |
| #include "libavcodec/bytestream.h" |
| #include "libavutil/refstruct.h" |
| |
| static int decode_film_grain_characteristics(H2645SEIFilmGrainCharacteristics *h, const SEIRawFilmGrainCharacteristics *s, const VVCFrameContext *fc) |
| { |
| const VVCSPS *sps = fc->ps.sps; |
| |
| h->present = !s->fg_characteristics_cancel_flag; |
| if (h->present) { |
| h->model_id = s->fg_model_id; |
| h->separate_colour_description_present_flag = s->fg_separate_colour_description_present_flag; |
| if (h->separate_colour_description_present_flag) { |
| h->bit_depth_luma = s->fg_bit_depth_luma_minus8 + 8; |
| h->bit_depth_chroma = s->fg_bit_depth_chroma_minus8 + 8; |
| h->full_range = s->fg_full_range_flag; |
| h->color_primaries = s->fg_colour_primaries; |
| h->transfer_characteristics = s->fg_transfer_characteristics; |
| h->matrix_coeffs = s->fg_matrix_coeffs; |
| } else { |
| if (!sps) { |
| av_log(fc->log_ctx, AV_LOG_ERROR, |
| "No active SPS for film_grain_characteristics.\n"); |
| return AVERROR_INVALIDDATA; |
| } |
| h->bit_depth_luma = sps->bit_depth; |
| h->bit_depth_chroma = sps->bit_depth; |
| h->full_range = sps->r->vui.vui_full_range_flag; |
| h->color_primaries = sps->r->vui.vui_colour_primaries; |
| h->transfer_characteristics = sps->r->vui.vui_transfer_characteristics; |
| h->matrix_coeffs = sps->r->vui.vui_matrix_coeffs ; |
| } |
| |
| h->blending_mode_id = s->fg_blending_mode_id; |
| h->log2_scale_factor = s->fg_log2_scale_factor; |
| |
| for (int c = 0; c < 3; c++) { |
| h->comp_model_present_flag[c] = s->fg_comp_model_present_flag[c]; |
| if (h->comp_model_present_flag[c]) { |
| h->num_intensity_intervals[c] = s->fg_num_intensity_intervals_minus1[c] + 1; |
| h->num_model_values[c] = s->fg_num_model_values_minus1[c] + 1; |
| |
| if (h->num_model_values[c] > 6) |
| return AVERROR_INVALIDDATA; |
| |
| for (int i = 0; i < h->num_intensity_intervals[c]; i++) { |
| h->intensity_interval_lower_bound[c][i] = s->fg_intensity_interval_lower_bound[c][i]; |
| h->intensity_interval_upper_bound[c][i] = s->fg_intensity_interval_upper_bound[c][i]; |
| for (int j = 0; j < h->num_model_values[c]; j++) |
| h->comp_model_value[c][i][j] = s->fg_comp_model_value[c][i][j]; |
| } |
| } |
| } |
| |
| h->persistence_flag = s->fg_characteristics_persistence_flag; |
| } |
| |
| return 0; |
| } |
| |
| static int decode_decoded_picture_hash(H274SEIPictureHash *h, const SEIRawDecodedPictureHash *s) |
| { |
| h->present = 1; |
| h->hash_type = s->dph_sei_hash_type; |
| if (h->hash_type == 0) |
| memcpy(h->md5, s->dph_sei_picture_md5, sizeof(h->md5)); |
| else if (h->hash_type == 1) |
| memcpy(h->crc, s->dph_sei_picture_crc, sizeof(h->crc)); |
| else if (h->hash_type == 2) |
| memcpy(h->checksum, s->dph_sei_picture_checksum, sizeof(h->checksum)); |
| |
| return 0; |
| } |
| |
| static int decode_display_orientation(H2645SEIDisplayOrientation *h, const SEIRawDisplayOrientation *s) |
| { |
| int degrees[] = { 0, 0x8000, 0x4000, 0xC000 }; |
| |
| h->present = !s->display_orientation_cancel_flag; |
| if (h->present) { |
| if (s->display_orientation_transform_type > 7) |
| return AVERROR_INVALIDDATA; |
| |
| h->vflip = 0; |
| if (s->display_orientation_transform_type == 1 || |
| s->display_orientation_transform_type == 3 || |
| s->display_orientation_transform_type == 4 || |
| s->display_orientation_transform_type == 6) { |
| h->hflip = 1; |
| } else { |
| h->hflip = 0; |
| } |
| h->anticlockwise_rotation = degrees[s->display_orientation_transform_type >> 1]; |
| } |
| |
| return 0; |
| } |
| |
| static int decode_content_light_level_info(H2645SEIContentLight *h, const SEIRawContentLightLevelInfo *s) |
| { |
| h->present = 1; |
| h->max_content_light_level = s->max_content_light_level; |
| h->max_pic_average_light_level = s->max_pic_average_light_level; |
| |
| return 0; |
| } |
| |
| static int decode_frame_field_info(H274SEIFrameFieldInfo *h, const SEIRawFrameFieldInformation *s) |
| { |
| if (s->ffi_source_scan_type > 3) |
| return AVERROR_INVALIDDATA; |
| |
| h->present = 1; |
| if (s->ffi_field_pic_flag) { |
| if (s->ffi_bottom_field_flag) |
| h->picture_struct = AV_PICTURE_STRUCTURE_BOTTOM_FIELD; |
| else |
| h->picture_struct = AV_PICTURE_STRUCTURE_TOP_FIELD; |
| } else { |
| h->display_elemental_periods = s->ffi_display_elemental_periods_minus1 + 1; |
| } |
| |
| h->source_scan_type = s->ffi_source_scan_type; |
| h->duplicate_flag = s->ffi_duplicate_flag; |
| |
| return 0; |
| } |
| |
| static int decode_ambient_viewing_environment(H2645SEIAmbientViewingEnvironment *h, const SEIRawAmbientViewingEnvironment *s) |
| { |
| h->present = 1; |
| h->ambient_illuminance = s->ambient_illuminance; |
| h->ambient_light_x = s->ambient_light_x; |
| h->ambient_light_y = s->ambient_light_y; |
| |
| return 0; |
| } |
| |
| static int decode_mastering_display_colour_volume(H2645SEIMasteringDisplay *h, const SEIRawMasteringDisplayColourVolume *s) |
| { |
| h->present = 1; |
| |
| for (int c = 0; c < 3; c++) { |
| h->display_primaries[c][0] = s->display_primaries_x[c]; |
| h->display_primaries[c][1] = s->display_primaries_y[c]; |
| } |
| |
| h->white_point[0] = s->white_point_x; |
| h->white_point[1] = s->white_point_y; |
| |
| h->max_luminance = s->max_display_mastering_luminance; |
| h->min_luminance = s->min_display_mastering_luminance; |
| |
| return 0; |
| } |
| |
| static int decode_user_data_registered_itu_t_t35(H2645SEI *sei, const SEIRawUserDataRegistered *s, |
| const VVCFrameContext *fc) |
| { |
| GetByteContext gbc; |
| int offset = (s->itu_t_t35_country_code == 0xff) + 1; |
| |
| bytestream2_init(&gbc, s->data_ref, s->data_length + offset); |
| return ff_h2645_sei_message_decode(sei, SEI_TYPE_USER_DATA_REGISTERED_ITU_T_T35, |
| AV_CODEC_ID_VVC, NULL, &gbc, fc->log_ctx); |
| } |
| |
| static int decode_user_data_uregistered(H2645SEI *sei, const SEIRawUserDataUnregistered *s, |
| const VVCFrameContext *fc) |
| { |
| GetByteContext gbc; |
| |
| bytestream2_init(&gbc, s->data_ref, s->data_length + 16); |
| return ff_h2645_sei_message_decode(sei, SEI_TYPE_USER_DATA_UNREGISTERED, |
| AV_CODEC_ID_VVC, NULL, &gbc, fc->log_ctx); |
| } |
| |
| int ff_vvc_sei_decode(VVCSEI *s, const H266RawSEI *sei, const struct VVCFrameContext *fc) |
| { |
| H2645SEI *c = &s->common; |
| |
| if (!sei) |
| return AVERROR_INVALIDDATA; |
| |
| for (int i = 0; i < sei->message_list.nb_messages; i++) { |
| int ret = 0; |
| SEIRawMessage *message = &sei->message_list.messages[i]; |
| void *payload = message->payload; |
| |
| switch (message->payload_type) { |
| case SEI_TYPE_FILM_GRAIN_CHARACTERISTICS: |
| av_refstruct_unref(&c->film_grain_characteristics); |
| c->film_grain_characteristics = av_refstruct_allocz(sizeof(*c->film_grain_characteristics)); |
| if (!c->film_grain_characteristics) |
| return AVERROR(ENOMEM); |
| ret = decode_film_grain_characteristics(c->film_grain_characteristics, payload, fc); |
| break; |
| |
| case SEI_TYPE_DECODED_PICTURE_HASH: |
| ret = decode_decoded_picture_hash(&s->picture_hash, payload); |
| break; |
| |
| case SEI_TYPE_DISPLAY_ORIENTATION: |
| ret = decode_display_orientation(&s->common.display_orientation, payload); |
| break; |
| |
| case SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO: |
| ret = decode_content_light_level_info(&s->common.content_light, payload); |
| break; |
| |
| case SEI_TYPE_FRAME_FIELD_INFO: |
| ret = decode_frame_field_info(&s->frame_field_info, payload); |
| break; |
| |
| case SEI_TYPE_AMBIENT_VIEWING_ENVIRONMENT: |
| ret = decode_ambient_viewing_environment(&s->common.ambient_viewing_environment, payload); |
| break; |
| |
| case SEI_TYPE_MASTERING_DISPLAY_COLOUR_VOLUME: |
| ret = decode_mastering_display_colour_volume(&s->common.mastering_display, payload); |
| break; |
| |
| case SEI_TYPE_USER_DATA_REGISTERED_ITU_T_T35: |
| ret = decode_user_data_registered_itu_t_t35(&s->common, payload, fc); |
| break; |
| |
| case SEI_TYPE_USER_DATA_UNREGISTERED: |
| ret = decode_user_data_uregistered(&s->common, payload, fc); |
| break; |
| |
| default: |
| av_log(fc->log_ctx, AV_LOG_DEBUG, "Skipped %s SEI %d\n", |
| sei->nal_unit_header.nal_unit_type == VVC_PREFIX_SEI_NUT ? |
| "PREFIX" : "SUFFIX", message->payload_type); |
| return FF_H2645_SEI_MESSAGE_UNHANDLED; |
| } |
| |
| if (ret == AVERROR(ENOMEM)) |
| return ret; |
| if (ret < 0) |
| av_log(fc->log_ctx, AV_LOG_WARNING, "Failure to parse %s SEI %d: %s\n", |
| sei->nal_unit_header.nal_unit_type == VVC_PREFIX_SEI_NUT ? |
| "PREFIX" : "SUFFIX", message->payload_type, av_err2str(ret)); |
| } |
| |
| return 0; |
| } |
| |
| int ff_vvc_sei_replace(VVCSEI *dst, const VVCSEI *src) |
| { |
| dst->picture_hash.present = 0; // drop hash |
| dst->frame_field_info.present = 0; // drop field info |
| return ff_h2645_sei_ctx_replace(&dst->common, &src->common); |
| } |
| |
| void ff_vvc_sei_reset(VVCSEI *s) |
| { |
| ff_h2645_sei_reset(&s->common); |
| s->picture_hash.present = 0; |
| s->frame_field_info.present = 0; |
| } |