| /* |
| * 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); |
| } |