/* * 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 "libavutil/buffer.h" #include "libavutil/frame.h" #include "libavutil/hdr_dynamic_metadata.h" #include "libavutil/internal.h" #include "libavutil/mem.h" #include "avcodec.h" #include "atsc_a53.h" #include "bytestream.h" #include "decode.h" #include "dynamic_hdr_vivid.h" #include "dovi_rpu.h" #include "itut35.h" #include "version.h" int ff_itut_t35_parse_buffer(FFITUTT35 *const itut_t35, const uint8_t *buf, size_t buf_size, int flags) { GetByteContext gb; int provider_code, country_code; unsigned int provider_oriented_code = 0; bytestream2_init(&gb, buf, buf_size); if (flags & FF_ITUT_T35_FLAG_COUNTRY_CODE) country_code = itut_t35->country_code; else { country_code = bytestream2_get_byte(&gb); if (country_code == 0xFF) { if (bytestream2_get_bytes_left(&gb) < 1) return AVERROR_INVALIDDATA; bytestream2_skipu(&gb, 1); // itu_t_t35_country_code_extension_byte } } switch (country_code) { case ITU_T_T35_COUNTRY_CODE_US: if (bytestream2_get_bytes_left(&gb) < 2) return AVERROR_INVALIDDATA; provider_code = bytestream2_get_be16u(&gb); switch (provider_code) { case ITU_T_T35_PROVIDER_CODE_ATSC: if (bytestream2_get_bytes_left(&gb) < 4) return AVERROR_INVALIDDATA; provider_oriented_code = bytestream2_get_be32u(&gb); switch (provider_oriented_code) { case MKBETAG('D', 'T', 'G', '1'): // afd_data if (bytestream2_get_bytes_left(&gb) < 2) return AVERROR_INVALIDDATA; if (!(bytestream2_get_byteu(&gb) & 0x40)) // active_format_flag return 0; //ignore break; case MKBETAG('G', 'A', '9', '4'): // closed captions break; default: // ignore unsupported identifiers return 0; } break; case ITU_T_T35_PROVIDER_CODE_AOM: if (bytestream2_get_bytes_left(&gb) < 1) return AVERROR_INVALIDDATA; provider_oriented_code = bytestream2_get_byteu(&gb); if (provider_oriented_code != 0x0001) return 0; // ignore break; case ITU_T_T35_PROVIDER_CODE_SAMSUNG: if (bytestream2_get_bytes_left(&gb) < 3) return AVERROR_INVALIDDATA; provider_oriented_code = bytestream2_get_be16u(&gb); int application_identifier = bytestream2_get_byteu(&gb); if (provider_oriented_code != 1 || application_identifier != 4) return 0; // ignore break; case ITU_T_T35_PROVIDER_CODE_DOLBY: if (bytestream2_get_bytes_left(&gb) < 4) return AVERROR_INVALIDDATA; provider_oriented_code = bytestream2_get_be32u(&gb); if (provider_oriented_code != 0x800) return 0; // ignore break; case ITU_T_T35_PROVIDER_CODE_SMPTE: if (bytestream2_get_bytes_left(&gb) < 2) return AVERROR_INVALIDDATA; provider_oriented_code = bytestream2_get_be16u(&gb); if (provider_oriented_code != 1) return 0; // ignore break; default: // ignore unsupported provider codes return 0; } break; case ITU_T_T35_COUNTRY_CODE_UK: if (bytestream2_get_bytes_left(&gb) < 3) return AVERROR_INVALIDDATA; bytestream2_skipu(&gb, 1); // t35_uk_country_code_second_octet provider_code = bytestream2_get_be16u(&gb); switch (provider_code) { case ITU_T_T35_PROVIDER_CODE_VNOVA: break; default: // ignore unsupported provider codes return 0; } break; case ITU_T_T35_COUNTRY_CODE_CN: if (bytestream2_get_bytes_left(&gb) < 2) return AVERROR_INVALIDDATA; provider_code = bytestream2_get_be16u(&gb); switch (provider_code) { case ITU_T_T35_PROVIDER_CODE_HDR_VIVID: if (bytestream2_get_bytes_left(&gb) < 2) return AVERROR_INVALIDDATA; provider_oriented_code = bytestream2_get_be16u(&gb); if (provider_oriented_code != 0x0005) return 0; //ignore break; default: // ignore unsupported provider codes return 0; } break; default: // ignore unsupported country codes return 0; } if (!bytestream2_get_bytes_left(&gb)) return AVERROR_INVALIDDATA; itut_t35->payload = gb.buffer; itut_t35->payload_size = bytestream2_get_bytes_left(&gb); itut_t35->country_code = country_code; itut_t35->provider_code = provider_code; itut_t35->provider_oriented_code = provider_oriented_code; return 1; } int ff_itut_t35_parse_payload_to_struct(FFITUTT35 *const itut_t35, FFITUTT35Aux *const aux, FFITUTT35Meta *metadata, int err_recognition) { int ret; switch (itut_t35->country_code) { case ITU_T_T35_COUNTRY_CODE_US: switch (itut_t35->provider_code) { case ITU_T_T35_PROVIDER_CODE_AOM: ff_aom_uninit_film_grain_params(&metadata->aom_film_grain); ret = ff_aom_parse_film_grain_sets(&metadata->aom_film_grain, itut_t35->payload, itut_t35->payload_size); if (ret < 0) return ret; break; case ITU_T_T35_PROVIDER_CODE_ATSC: switch (itut_t35->provider_oriented_code) { case MKBETAG('D', 'T', 'G', '1'): // afd_data av_buffer_unref(&metadata->afd); metadata->afd = av_buffer_alloc(1); if (!metadata->afd) return AVERROR(ENOMEM); *metadata->afd->data = *itut_t35->payload & 0xf; break; case MKBETAG('G', 'A', '9', '4'): // closed captions ret = ff_parse_a53_cc(&metadata->a53_cc, itut_t35->payload, itut_t35->payload_size); if (ret < 0) return ret; break; default: // ignore unsupported identifiers break; } break; case ITU_T_T35_PROVIDER_CODE_SAMSUNG: { size_t size; AVDynamicHDRPlus *hdr_plus = av_dynamic_hdr_plus_alloc(&size); if (!hdr_plus) return AVERROR(ENOMEM); ret = av_dynamic_hdr_plus_from_t35(hdr_plus, itut_t35->payload, itut_t35->payload_size); if (ret < 0) return ret; av_buffer_unref(&metadata->hdr_plus); metadata->hdr_plus = av_buffer_create((uint8_t *)hdr_plus, size, NULL, NULL, 0); if (!metadata->hdr_plus) { av_free(hdr_plus); return AVERROR(ENOMEM); } break; } case ITU_T_T35_PROVIDER_CODE_DOLBY: { AVDOVIMetadata *dovi; if (!aux || !aux->dovi) return 0; // ignore ret = ff_dovi_rpu_parse(aux->dovi, itut_t35->payload, itut_t35->payload_size, err_recognition); if (ret < 0) return 0; // ignore ret = ff_dovi_get_metadata(aux->dovi, &dovi); if (ret <= 0) return ret; av_buffer_unref(&metadata->dovi); metadata->dovi = av_buffer_create((uint8_t *)dovi, ret, NULL, NULL, 0); if (!metadata->dovi) { av_free(dovi); return AVERROR(ENOMEM); } break; } case ITU_T_T35_PROVIDER_CODE_SMPTE: { size_t size; AVDynamicHDRSmpte2094App5 *hdr_smpte2094_app5 = av_dynamic_hdr_smpte2094_app5_alloc(&size); if (!hdr_smpte2094_app5) return AVERROR(ENOMEM); ret = av_dynamic_hdr_smpte2094_app5_from_t35(hdr_smpte2094_app5, itut_t35->payload, itut_t35->payload_size); if (ret < 0) return ret; av_buffer_unref(&metadata->hdr_smpte2094_app5); metadata->hdr_smpte2094_app5 = av_buffer_create((uint8_t *)hdr_smpte2094_app5, size, NULL, NULL, 0); if (!metadata->hdr_smpte2094_app5) { av_free(hdr_smpte2094_app5); return AVERROR(ENOMEM); } break; } default: break; } break; case ITU_T_T35_COUNTRY_CODE_UK: switch (itut_t35->provider_code) { case ITU_T_T35_PROVIDER_CODE_VNOVA: av_buffer_unref(&metadata->lcevc); metadata->lcevc = av_buffer_alloc(itut_t35->payload_size); if (!metadata->lcevc) return AVERROR(ENOMEM); memcpy(metadata->lcevc->data, itut_t35->payload, itut_t35->payload_size); break; default: break; } break; case ITU_T_T35_COUNTRY_CODE_CN: switch (itut_t35->provider_code) { case ITU_T_T35_PROVIDER_CODE_HDR_VIVID: { size_t size; AVDynamicHDRVivid *hdr_vivid = av_dynamic_hdr_vivid_alloc(&size); if (!hdr_vivid) return AVERROR(ENOMEM); ret = ff_parse_itu_t_t35_to_dynamic_hdr_vivid(hdr_vivid, itut_t35->payload, itut_t35->payload_size); if (ret < 0) { av_free(hdr_vivid); return ret; } av_buffer_unref(&metadata->hdr_vivid); metadata->hdr_vivid = av_buffer_create((uint8_t *)hdr_vivid, size, NULL, NULL, 0); if (!metadata->hdr_vivid) { av_free(hdr_vivid); return AVERROR(ENOMEM); } break; } default: break; } break; default: // ignore unsupported provider codes break; } return 0; } int ff_itut_t35_parse_payload_to_frame(FFITUTT35 *const itut_t35, FFITUTT35Aux *const aux, AVCodecContext *const avctx, AVFrame *const frame) { FFITUTT35Meta metadata = { 0 }; int ret; ret = ff_itut_t35_parse_payload_to_struct(itut_t35, aux, &metadata, avctx->err_recognition); if (ret < 0) return ret; if (metadata.afd) { if (!av_frame_new_side_data_from_buf(frame, AV_FRAME_DATA_AFD, metadata.afd)) av_buffer_unref(&metadata.afd); metadata.afd = NULL; } if (metadata.a53_cc) { ret = ff_frame_new_side_data_from_buf(avctx, frame, AV_FRAME_DATA_A53_CC, &metadata.a53_cc); if (ret < 0) return ret; #if FF_API_CODEC_PROPS FF_DISABLE_DEPRECATION_WARNINGS avctx->properties |= FF_CODEC_PROPERTY_CLOSED_CAPTIONS; FF_ENABLE_DEPRECATION_WARNINGS #endif } if (metadata.aom_film_grain.enable) { ret = ff_aom_attach_film_grain_sets(&metadata.aom_film_grain, frame); if (ret < 0) return ret; } if (metadata.hdr_plus) { ret = ff_frame_new_side_data_from_buf(avctx, frame, AV_FRAME_DATA_DYNAMIC_HDR_PLUS, &metadata.hdr_plus); if (ret < 0) return ret; } if (metadata.dovi) { AVFrameSideData *sd = av_frame_new_side_data_from_buf(frame, AV_FRAME_DATA_DOVI_METADATA, metadata.dovi); if (!sd) { av_buffer_unref(&metadata.dovi); return AVERROR(ENOMEM); } metadata.dovi = NULL; } if (metadata.hdr_smpte2094_app5) { ret = ff_frame_new_side_data_from_buf(avctx, frame, AV_FRAME_DATA_DYNAMIC_HDR_SMPTE_2094_APP5, &metadata.hdr_smpte2094_app5); if (ret < 0) return ret; } if (metadata.lcevc) { ret = ff_frame_new_side_data_from_buf(avctx, frame, AV_FRAME_DATA_LCEVC, &metadata.lcevc); if (ret < 0) return ret; } if (metadata.hdr_vivid) { ret = ff_frame_new_side_data_from_buf(avctx, frame, AV_FRAME_DATA_DYNAMIC_HDR_VIVID, &metadata.hdr_vivid); if (ret < 0) return ret; } ff_itut_t35_unref(&metadata); return 0; } void ff_itut_t35_unref(FFITUTT35Meta *metadata) { ff_aom_uninit_film_grain_params(&metadata->aom_film_grain); av_buffer_unref(&metadata->afd); av_buffer_unref(&metadata->a53_cc); av_buffer_unref(&metadata->hdr_plus); av_buffer_unref(&metadata->hdr_smpte2094_app5); av_buffer_unref(&metadata->lcevc); av_buffer_unref(&metadata->dovi); av_buffer_unref(&metadata->hdr_vivid); }