This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit 4fcd2349ffabe85baa84177761496aa38478c803 Author: James Almer <[email protected]> AuthorDate: Fri May 29 12:09:11 2026 -0300 Commit: James Almer <[email protected]> CommitDate: Tue Jun 2 19:50:39 2026 -0300 avcodec/itut35: add ITU-T T35 parsing helpers Signed-off-by: James Almer <[email protected]> --- configure | 2 + libavcodec/Makefile | 1 + libavcodec/itut35.c | 303 ++++++++++++++++++++++++++++++++++++++++++++++++++++ libavcodec/itut35.h | 94 ++++++++++++++++ 4 files changed, 400 insertions(+) diff --git a/configure b/configure index c22cbe72f4..a1afc9366b 100755 --- a/configure +++ b/configure @@ -2796,6 +2796,7 @@ CONFIG_EXTRA=" intrax8 iso_media iso_writer + itut_t35 ividsp jpegtables lgplv3 @@ -3099,6 +3100,7 @@ iamfenc_deps="iamf" inflate_wrapper_deps="zlib" intrax8_select="blockdsp wmv2dsp" iso_media_select="mpeg4audio" +itut_t35_select="atsc_a53 dovi_rpudec" me_cmp_select="idctdsp" mpeg_er_select="error_resilience" mpegaudio_select="mpegaudiodsp mpegaudioheader" diff --git a/libavcodec/Makefile b/libavcodec/Makefile index ac3c4d1978..120722dc40 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -126,6 +126,7 @@ OBJS-$(CONFIG_HUFFYUVENCDSP) += huffyuvencdsp.o OBJS-$(CONFIG_IDCTDSP) += idctdsp.o simple_idct.o jrevdct.o OBJS-$(CONFIG_INFLATE_WRAPPER) += zlib_wrapper.o OBJS-$(CONFIG_INTRAX8) += intrax8.o intrax8dsp.o msmpeg4_vc1_data.o +OBJS-$(CONFIG_ITUT_T35) += itut35.o OBJS-$(CONFIG_IVIDSP) += ivi_dsp.o OBJS-$(CONFIG_JNI) += ffjni.o jni.o OBJS-$(CONFIG_JPEGTABLES) += jpegtables.o diff --git a/libavcodec/itut35.c b/libavcodec/itut35.c new file mode 100644 index 0000000000..99dd63457e --- /dev/null +++ b/libavcodec/itut35.c @@ -0,0 +1,303 @@ +/* + * 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 "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('G', 'A', '9', '4'): // closed captions + break; + default: // ignore unsupported identifiers + return 0; + } + 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; + + 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_ATSC: + switch (itut_t35->provider_oriented_code) { + 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; + + 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; + + 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; + + 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: + 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; + + 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.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.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; + } + + ff_itut_t35_unref(&metadata); + + return 0; +} + +void ff_itut_t35_unref(FFITUTT35Meta *metadata) +{ + 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); +} diff --git a/libavcodec/itut35.h b/libavcodec/itut35.h index 3820fcfec4..e92e086e9c 100644 --- a/libavcodec/itut35.h +++ b/libavcodec/itut35.h @@ -19,6 +19,13 @@ #ifndef AVCODEC_ITUT35_H #define AVCODEC_ITUT35_H +#include <stdint.h> +#include <stddef.h> + +#include "libavutil/frame.h" +#include "avcodec.h" +#include "dovi_rpu.h" + #define ITU_T_T35_COUNTRY_CODE_CN 0x26 #define ITU_T_T35_COUNTRY_CODE_UK 0xB4 #define ITU_T_T35_COUNTRY_CODE_US 0xB5 @@ -39,4 +46,91 @@ #define ITU_T_T35_PROVIDER_CODE_SAMSUNG 0x003C #define ITU_T_T35_PROVIDER_CODE_SMPTE 0x0090 +typedef struct FFITUTT35 { + int country_code; + + int provider_code; + unsigned int provider_oriented_code; + + const uint8_t *payload; + size_t payload_size; +} FFITUTT35; + +typedef struct FFITUTT35Meta { + AVBufferRef *a53_cc; + AVBufferRef *lcevc; + AVBufferRef *hdr_plus; + AVBufferRef *hdr_smpte2094_app5; + AVBufferRef *dovi; +} FFITUTT35Meta; + +typedef struct FFITUTT35Aux { + /** + * A DOVIContext. Must be set to a valid pointer in order to be parsed + * and filled. + */ + DOVIContext *dovi; +} FFITUTT35Aux; + +/** + * country_code is assumed to not be the first byte of the buffer and must + * be set by the caller beforehand. + */ +#define FF_ITUT_T35_FLAG_COUNTRY_CODE (1 << 0) + +/** + * Parse a raw ITU-T T35 buffer to get the country code, provider code, + * and set them plus the pointer and size in the FFITUTT35 struct to the + * start of the actual payload. + * + * @param itut_t35 The struct to fill + * @param buf The input buffer + * @param size Size of the input buffer + * @param flags A combination of FF_ITUT_T35_FLAG_* + * @return 0 if nothing was done (e.g. the payload is of an unsupported + * type), 1 on success, or a negative AVERROR code on failure + * + * @note buf will remain owned by the caller, and no new allocations will + * be made. Any pointer in the resulting struct will be valid as long + * as buf is valid. + */ +int ff_itut_t35_parse_buffer(FFITUTT35 *itut_t35, const uint8_t *buf, + size_t size, int flags); + +/** + * Parse a pre-processed ITU-T T35 payload to fill the metadata struct. + * + * @param itut_t35 The pre-filled struct + * @param aux A struct containing extra contexts required by certain + * payload types. Any pointer present is owned by the caller. + * May be NULL, in which case the relevant payloads will not + * be parsed. + * @param metadata A metadata struct. All the allocated buffer references + * are owned by the caller and must be freed accordingly. + * @param err_recognition A combination of AV_EF_* flags + * @return 0 on success, or a negative AVERROR code on failure + */ +int ff_itut_t35_parse_payload_to_struct(FFITUTT35 *itut_t35, FFITUTT35Aux *aux, + FFITUTT35Meta *metadata, int err_recognition); + +/** + * Parse a pre-processed ITU-T T35 payload to fill a frame's side data. + * + * @param itut_t35 The pre-filled struct + * @param aux A struct containing extra contexts required by certain + * payload types. Any pointer present is owned by the caller. + * May be NULL, in which case the relevant payloads will not + * be parsed. + * @param avctx The context that generated the frame + * @param frame A frame + * @return 0 on success, or a negative AVERROR code on failure + */ +int ff_itut_t35_parse_payload_to_frame(FFITUTT35 *itut_t35, FFITUTT35Aux *aux, + AVCodecContext *avctx, AVFrame *frame); + +/** + * Unref all references in metadata + */ +void ff_itut_t35_unref(FFITUTT35Meta *metadata); + #endif /* AVCODEC_ITUT35_H */ _______________________________________________ ffmpeg-cvslog mailing list -- [email protected] To unsubscribe send an email to [email protected]
