You've already forked FFmpeg
mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2025-12-25 22:17:24 +02:00
285 lines
10 KiB
C
285 lines
10 KiB
C
/*
|
|
* 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;
|
|
}
|