diff options
author | Jacob Lifshay <programmerjake@gmail.com> | 2025-07-22 23:07:14 -0700 |
---|---|---|
committer | Jacob Lifshay <programmerjake@gmail.com> | 2025-08-08 03:04:42 -0700 |
commit | 1c85a3832afd39caac5ca76436162f790849e46b (patch) | |
tree | dd9fefd5a4c284546bb0b16205e9047409eaf94d | |
parent | a566fcb9dc07262b0c6575466ce8cbc8b667e5b2 (diff) | |
download | ffmpeg-1c85a3832afd39caac5ca76436162f790849e46b.tar.gz |
lavc: add API for manipulating SMPTE-436M VBI/ANC data
Signed-off-by: Jacob Lifshay <programmerjake@gmail.com>
-rw-r--r-- | .forgejo/pre-commit/ignored-words.txt | 1 | ||||
-rwxr-xr-x | configure | 1 | ||||
-rw-r--r-- | doc/APIchanges | 4 | ||||
-rw-r--r-- | libavcodec/Makefile | 2 | ||||
-rw-r--r-- | libavcodec/smpte_436m.c | 475 | ||||
-rw-r--r-- | libavcodec/smpte_436m.h | 254 | ||||
-rw-r--r-- | libavcodec/smpte_436m_internal.h | 98 |
7 files changed, 835 insertions, 0 deletions
diff --git a/.forgejo/pre-commit/ignored-words.txt b/.forgejo/pre-commit/ignored-words.txt index 9330ee43ef..870fd96be3 100644 --- a/.forgejo/pre-commit/ignored-words.txt +++ b/.forgejo/pre-commit/ignored-words.txt @@ -8,6 +8,7 @@ ALOG ALS als ANC +anc ANS ans anull @@ -2651,6 +2651,7 @@ CONFIG_EXTRA=" rv34dsp scene_sad sinewin + smpte_436m snappy srtp startcode diff --git a/doc/APIchanges b/doc/APIchanges index 7443884885..0bdfb5cfbb 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -2,6 +2,10 @@ The last version increases of all libraries were on 2025-03-28 API changes, most recent first: +2025-07-29 - xxxxxxxxxx - lavc 62.10.100 - smpte_436m.h + Add a new public header smpte_436m.h with API for + manipulating AV_CODEC_ID_SMPTE_436M_ANC data. + 2025-07-10 - xxxxxxxxxx - lavf 62.2.100 mxf [de]muxer now uses AV_CODEC_ID_SMPTE_436M_ANC for the vbi_vanc_smpte_436M streams instead of AV_CODEC_ID_NONE. diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 33add16045..83ef92963a 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -19,6 +19,7 @@ HEADERS = ac3_parser.h \ mediacodec.h \ packet.h \ qsv.h \ + smpte_436m.h \ vdpau.h \ version.h \ version_major.h \ @@ -167,6 +168,7 @@ OBJS-$(CONFIG_QSVENC) += qsvenc.o OBJS-$(CONFIG_RANGECODER) += rangecoder.o OBJS-$(CONFIG_RV34DSP) += rv34dsp.o OBJS-$(CONFIG_SINEWIN) += sinewin.o +OBJS-$(CONFIG_SMPTE_436M) += smpte_436m.o OBJS-$(CONFIG_SNAPPY) += snappy.o OBJS-$(CONFIG_STARTCODE) += startcode.o OBJS-$(CONFIG_TEXTUREDSP) += texturedsp.o diff --git a/libavcodec/smpte_436m.c b/libavcodec/smpte_436m.c new file mode 100644 index 0000000000..b2324cac18 --- /dev/null +++ b/libavcodec/smpte_436m.c @@ -0,0 +1,475 @@ +/* + * MXF SMPTE-436M VBI/ANC parsing functions + * Copyright (c) 2025 Jacob Lifshay + * + * 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 "libavcodec/smpte_436m.h" +#include "bytestream.h" +#include "libavcodec/packet.h" +#include "libavutil/avassert.h" +#include "libavutil/error.h" +#include "libavutil/intreadwrite.h" + +static int validate_smpte_436m_anc_wrapping_type(AVSmpte436mWrappingType wrapping_type) +{ + switch (wrapping_type) { + case AV_SMPTE_436M_WRAPPING_TYPE_VANC_FRAME: + case AV_SMPTE_436M_WRAPPING_TYPE_VANC_FIELD_1: + case AV_SMPTE_436M_WRAPPING_TYPE_VANC_FIELD_2: + case AV_SMPTE_436M_WRAPPING_TYPE_VANC_PROGRESSIVE_FRAME: + case AV_SMPTE_436M_WRAPPING_TYPE_HANC_FRAME: + case AV_SMPTE_436M_WRAPPING_TYPE_HANC_FIELD_1: + case AV_SMPTE_436M_WRAPPING_TYPE_HANC_FIELD_2: + case AV_SMPTE_436M_WRAPPING_TYPE_HANC_PROGRESSIVE_FRAME: + return 0; + default: + return AVERROR_INVALIDDATA; + } +} + +static int validate_smpte_436m_anc_payload_sample_coding(AVSmpte436mPayloadSampleCoding payload_sample_coding) +{ + switch (payload_sample_coding) { + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_1BIT_LUMA: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_1BIT_COLOR_DIFF: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_1BIT_LUMA_AND_COLOR_DIFF: + // not allowed for ANC packets + return AVERROR_INVALIDDATA; + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_COLOR_DIFF: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA_AND_COLOR_DIFF: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_10BIT_LUMA: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_10BIT_COLOR_DIFF: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_10BIT_LUMA_AND_COLOR_DIFF: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA_WITH_PARITY_ERROR: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_COLOR_DIFF_WITH_PARITY_ERROR: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA_AND_COLOR_DIFF_WITH_PARITY_ERROR: + return 0; + default: + return AVERROR_INVALIDDATA; + } +} + +int av_smpte_436m_coded_anc_validate(const AVSmpte436mCodedAnc *anc) +{ + int ret = validate_smpte_436m_anc_wrapping_type(anc->wrapping_type); + if (ret < 0) + return ret; + ret = validate_smpte_436m_anc_payload_sample_coding(anc->payload_sample_coding); + if (ret < 0) + return ret; + if (anc->payload_array_length > AV_SMPTE_436M_CODED_ANC_PAYLOAD_CAPACITY) + return AVERROR_INVALIDDATA; + ret = av_smpte_436m_coded_anc_payload_size(anc->payload_sample_coding, anc->payload_sample_count); + if (ret < 0) + return ret; + if (anc->payload_array_length < ret) + return AVERROR_INVALIDDATA; + return 0; +} + +// Based off Table 7 (page 13) of: +// https://pub.smpte.org/latest/st436/s436m-2006.pdf +#define SMPTE_436M_ANC_ENTRY_HEADER_SIZE ( \ + 2 /* line_number */ \ + + 1 /* wrapping_type */ \ + + 1 /* payload_sample_coding */ \ + + 2 /* payload_sample_count */ \ + + 4 /* payload_array_length */ \ + + 4 /* payload_array_element_size */ \ +) + +/** + * Decode an ANC packet. + * @param[in] in Input bytes. + * @param[in] size the size of in. + * @param[out] anc the decoded ANC packet + * @return The number of read bytes on success, AVERROR_INVALIDDATA otherwise. + */ +static int smpte_436m_anc_decode_entry(const uint8_t *in, int size, AVSmpte436mCodedAnc *anc) +{ + // Based off Table 7 (page 13) of: + // https://pub.smpte.org/latest/st436/s436m-2006.pdf + if (SMPTE_436M_ANC_ENTRY_HEADER_SIZE > size) + return AVERROR_INVALIDDATA; + int needed_size = SMPTE_436M_ANC_ENTRY_HEADER_SIZE; + anc->line_number = AV_RB16(in); + in += 2; + anc->wrapping_type = AV_RB8(in); + in++; + anc->payload_sample_coding = AV_RB8(in); + in++; + anc->payload_sample_count = AV_RB16(in); + in += 2; + anc->payload_array_length = AV_RB32(in); + in += 4; + uint32_t payload_array_element_size = AV_RB32(in); + in += 4; + if (payload_array_element_size != 1) + return AVERROR_INVALIDDATA; + needed_size += anc->payload_array_length; + if (needed_size > size) + return AVERROR_INVALIDDATA; + if (anc->payload_array_length > AV_SMPTE_436M_CODED_ANC_PAYLOAD_CAPACITY) + return AVERROR_INVALIDDATA; + memcpy(anc->payload, in, anc->payload_array_length); + int ret = av_smpte_436m_coded_anc_validate(anc); + if (ret < 0) + return ret; + return needed_size; +} + +/** + * Encode an ANC packet. + * @param[in] anc the ANC packet to encode + * @param[in] size the size of out. ignored if out is NULL. + * @param[out] out Output bytes. Doesn't write anything if out is NULL. + * @return the number of bytes written on success, AVERROR codes otherwise. + * If out is NULL, returns the number of bytes it would have written. + */ +static int smpte_436m_anc_encode_entry(uint8_t *out, int size, const AVSmpte436mCodedAnc *anc) +{ + // Based off Table 7 (page 13) of: + // https://pub.smpte.org/latest/st436/s436m-2006.pdf + if (anc->payload_array_length > AV_SMPTE_436M_CODED_ANC_PAYLOAD_CAPACITY) + return AVERROR_INVALIDDATA; + int needed_size = SMPTE_436M_ANC_ENTRY_HEADER_SIZE + (int)anc->payload_array_length; + if (!out) + return needed_size; + if (needed_size > size) + return AVERROR_BUFFER_TOO_SMALL; + AV_WB16(out, anc->line_number); + out += 2; + AV_WB8(out, anc->wrapping_type); + out++; + AV_WB8(out, anc->payload_sample_coding); + out++; + AV_WB16(out, anc->payload_sample_count); + out += 2; + AV_WB32(out, anc->payload_array_length); + out += 4; + AV_WB32(out, 1); // payload_array_element_size + out += 4; + memcpy(out, anc->payload, anc->payload_array_length); + return needed_size; +} + +int av_smpte_436m_anc_encode(uint8_t *out, int size, int anc_packet_count, const AVSmpte436mCodedAnc *anc_packets) +{ + // Based off Table 7 (page 13) of: + // https://pub.smpte.org/latest/st436/s436m-2006.pdf + if (anc_packet_count < 0 || anc_packet_count >= (1L << 16) || size < 0) + return AVERROR_INVALIDDATA; + + int needed_size = 2; + if (out) { + if (size < needed_size) + return AVERROR_BUFFER_TOO_SMALL; + AV_WB16(out, anc_packet_count); + out += 2; + size -= 2; + } + for (int i = 0; i < anc_packet_count; i++) { + int ret = smpte_436m_anc_encode_entry(out, size, &anc_packets[i]); + if (ret < 0) + return ret; + needed_size += ret; + if (out) { + size -= ret; + out += ret; + } + } + return needed_size; +} + +int av_smpte_436m_anc_append(AVPacket *pkt, int anc_packet_count, const AVSmpte436mCodedAnc *anc_packets) +{ + int final_packet_count = 0; + int write_start = 2; + if (pkt->size >= 2) { + final_packet_count = AV_RB16(pkt->data); + write_start = pkt->size; + } else if (pkt->size != 0) // if packet isn't empty + return AVERROR_INVALIDDATA; + if (anc_packet_count < 0 || anc_packet_count >= (1L << 16)) + return AVERROR_INVALIDDATA; + final_packet_count += anc_packet_count; + if (final_packet_count >= (1L << 16)) + return AVERROR_INVALIDDATA; + int ret, additional_size = write_start - pkt->size; + for (int i = 0; i < anc_packet_count; i++) { + ret = smpte_436m_anc_encode_entry(NULL, 0, &anc_packets[i]); + if (ret < 0) + return ret; + additional_size += ret; + } + ret = av_grow_packet(pkt, additional_size); + if (ret < 0) + return ret; + for (int i = 0; i < anc_packet_count; i++) { + ret = smpte_436m_anc_encode_entry(pkt->data + write_start, pkt->size - write_start, &anc_packets[i]); + av_assert0(ret >= 0); + write_start += ret; + } + AV_WB16(pkt->data, final_packet_count); + return 0; +} + +int av_smpte_436m_anc_iter_init(AVSmpte436mAncIterator *iter, const uint8_t *buf, int buf_size) +{ + // Based off Table 7 (page 13) of: + // https://pub.smpte.org/latest/st436/s436m-2006.pdf + if (buf_size < 2) + return AVERROR_INVALIDDATA; + *iter = (AVSmpte436mAncIterator){ + .anc_packets_left = AV_RB16(buf), + .size_left = buf_size - 2, + .data_left = buf + 2, + }; + if (iter->anc_packets_left > iter->size_left) + return AVERROR_INVALIDDATA; + return 0; +} + +int av_smpte_436m_anc_iter_next(AVSmpte436mAncIterator *iter, AVSmpte436mCodedAnc *anc) +{ + if (iter->anc_packets_left <= 0) + return AVERROR_EOF; + iter->anc_packets_left--; + int ret = smpte_436m_anc_decode_entry(iter->data_left, iter->size_left, anc); + if (ret < 0) { + iter->anc_packets_left = 0; + return ret; + } + iter->data_left += ret; + iter->size_left -= ret; + return 0; +} + +int av_smpte_436m_coded_anc_payload_size(AVSmpte436mPayloadSampleCoding sample_coding, uint16_t sample_count) +{ + if (sample_count > AV_SMPTE_436M_CODED_ANC_SAMPLE_CAPACITY) + return AVERROR_INVALIDDATA; + switch (sample_coding) { + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_1BIT_LUMA: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_1BIT_COLOR_DIFF: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_1BIT_LUMA_AND_COLOR_DIFF: + return AVERROR_INVALIDDATA; + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_COLOR_DIFF: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA_AND_COLOR_DIFF: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA_WITH_PARITY_ERROR: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_COLOR_DIFF_WITH_PARITY_ERROR: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA_AND_COLOR_DIFF_WITH_PARITY_ERROR: + // "The Payload Byte Array shall be padded to achieve UInt32 alignment." + // section 4.4 of https://pub.smpte.org/latest/st436/s436m-2006.pdf + return (sample_count + 3) & -4; + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_10BIT_LUMA: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_10BIT_COLOR_DIFF: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_10BIT_LUMA_AND_COLOR_DIFF: + // encoded with 3 10-bit samples in a UInt32. + // "The Payload Byte Array shall be padded to achieve UInt32 alignment." + // section 4.4 of https://pub.smpte.org/latest/st436/s436m-2006.pdf + return 4 * ((sample_count + 2) / 3); + default: + return AVERROR_INVALIDDATA; + } +} + +int av_smpte_291m_anc_8bit_decode(AVSmpte291mAnc8bit *out, + AVSmpte436mPayloadSampleCoding sample_coding, + uint16_t sample_count, + const uint8_t *payload, + void *log_ctx) +{ + switch (sample_coding) { + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_1BIT_LUMA: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_1BIT_COLOR_DIFF: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_1BIT_LUMA_AND_COLOR_DIFF: + return AVERROR_INVALIDDATA; + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_COLOR_DIFF: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA_AND_COLOR_DIFF: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA_WITH_PARITY_ERROR: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_COLOR_DIFF_WITH_PARITY_ERROR: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA_AND_COLOR_DIFF_WITH_PARITY_ERROR: + { + if (sample_count < 3) + return AVERROR_INVALIDDATA; + out->did = *payload++; + out->sdid_or_dbn = *payload++; + out->data_count = *payload++; + if (sample_count < out->data_count + 3) + return AVERROR_INVALIDDATA; + memcpy(out->payload, payload, out->data_count); + // the checksum isn't stored in 8-bit mode, so calculate it. + av_smpte_291m_anc_8bit_fill_checksum(out); + return 0; + } + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_10BIT_LUMA: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_10BIT_COLOR_DIFF: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_10BIT_LUMA_AND_COLOR_DIFF: + av_log(log_ctx, + AV_LOG_ERROR, + "decoding an ANC packet using the 10-bit SMPTE 436M sample coding isn't implemented.\n"); + return AVERROR_PATCHWELCOME; + default: + return AVERROR_INVALIDDATA; + } +} + +void av_smpte_291m_anc_8bit_fill_checksum(AVSmpte291mAnc8bit *anc) +{ + uint8_t checksum = anc->did + anc->sdid_or_dbn + anc->data_count; + for (unsigned i = 0; i < anc->data_count; i++) { + checksum += anc->payload[i]; + } + anc->checksum = checksum; +} + +int av_smpte_291m_anc_8bit_get_sample_count(const AVSmpte291mAnc8bit *anc, + AVSmpte436mPayloadSampleCoding sample_coding, + void *log_ctx) +{ + switch (sample_coding) { + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_1BIT_LUMA: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_1BIT_COLOR_DIFF: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_1BIT_LUMA_AND_COLOR_DIFF: + return AVERROR_INVALIDDATA; + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_COLOR_DIFF: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA_AND_COLOR_DIFF: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA_WITH_PARITY_ERROR: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_COLOR_DIFF_WITH_PARITY_ERROR: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA_AND_COLOR_DIFF_WITH_PARITY_ERROR: + // 3 for did, sdid_or_dbn, and data_count; checksum isn't stored in 8-bit modes + return 3 + anc->data_count; + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_10BIT_LUMA: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_10BIT_COLOR_DIFF: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_10BIT_LUMA_AND_COLOR_DIFF: + av_log(log_ctx, + AV_LOG_ERROR, + "encoding an ANC packet using the 10-bit SMPTE 436M sample coding isn't implemented.\n"); + return AVERROR_PATCHWELCOME; + default: + return AVERROR_INVALIDDATA; + } +} + +int av_smpte_291m_anc_8bit_encode(AVSmpte436mCodedAnc *out, + uint16_t line_number, + AVSmpte436mWrappingType wrapping_type, + AVSmpte436mPayloadSampleCoding sample_coding, + const AVSmpte291mAnc8bit *payload, + void *log_ctx) +{ + out->line_number = line_number; + out->wrapping_type = wrapping_type; + out->payload_sample_coding = sample_coding; + + int ret = av_smpte_291m_anc_8bit_get_sample_count(payload, sample_coding, log_ctx); + if (ret < 0) + return ret; + + out->payload_sample_count = ret; + + ret = av_smpte_436m_coded_anc_payload_size(sample_coding, out->payload_sample_count); + if (ret < 0) + return ret; + + out->payload_array_length = ret; + + switch (sample_coding) { + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_1BIT_LUMA: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_1BIT_COLOR_DIFF: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_1BIT_LUMA_AND_COLOR_DIFF: + return AVERROR_INVALIDDATA; + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_COLOR_DIFF: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA_AND_COLOR_DIFF: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA_WITH_PARITY_ERROR: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_COLOR_DIFF_WITH_PARITY_ERROR: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA_AND_COLOR_DIFF_WITH_PARITY_ERROR: + { + // fill trailing padding with zeros + av_assert0(out->payload_array_length >= 4); + memset(out->payload + out->payload_array_length - 4, 0, 4); + + out->payload[0] = payload->did; + out->payload[1] = payload->sdid_or_dbn; + out->payload[2] = payload->data_count; + + memcpy(out->payload + 3, payload->payload, payload->data_count); + return 0; + } + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_10BIT_LUMA: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_10BIT_COLOR_DIFF: + case AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_10BIT_LUMA_AND_COLOR_DIFF: + av_log(log_ctx, + AV_LOG_ERROR, + "encoding an ANC packet using the 10-bit SMPTE 436M sample coding isn't implemented.\n"); + return AVERROR_PATCHWELCOME; + default: + return AVERROR_INVALIDDATA; + } +} + +int av_smpte_291m_anc_8bit_extract_cta_708(const AVSmpte291mAnc8bit *anc, uint8_t *cc_data, void *log_ctx) +{ + if (anc->did != AV_SMPTE_291M_ANC_DID_CTA_708 || anc->sdid_or_dbn != AV_SMPTE_291M_ANC_SDID_CTA_708) + return AVERROR(EAGAIN); + GetByteContext gb; + bytestream2_init(&gb, anc->payload, anc->data_count); + // based on Caption Distribution Packet (CDP) Definition: + // https://pub.smpte.org/latest/st334-2/st0334-2-2015.pdf + uint16_t cdp_identifier = bytestream2_get_be16(&gb); + if (cdp_identifier != 0x9669) { // CDPs always have this value + av_log(log_ctx, AV_LOG_ERROR, "wrong cdp identifier %x\n", cdp_identifier); + return AVERROR_INVALIDDATA; + } + bytestream2_get_byte(&gb); // cdp_length + bytestream2_get_byte(&gb); // cdp_frame_rate and reserved + bytestream2_get_byte(&gb); // flags + bytestream2_get_be16(&gb); // cdp_hdr_sequence_cntr + unsigned section_id = bytestream2_get_byte(&gb); + + const unsigned TIME_CODE_SECTION_ID = 0x71; + if (section_id == TIME_CODE_SECTION_ID) { + bytestream2_skip(&gb, 4); // skip time code section + section_id = bytestream2_get_byte(&gb); + } + const unsigned CC_DATA_SECTION_ID = 0x72; + if (section_id == CC_DATA_SECTION_ID) { + if (bytestream2_get_bytes_left(&gb) < 1) + goto too_short; + // 0x1F for lower 5 bits, upper 3 bits are marker bits + unsigned cc_count = bytestream2_get_byte(&gb) & 0x1F; + unsigned data_length = cc_count * 3; // EIA-608/CTA-708 triples are 3 bytes long + if (bytestream2_get_bytes_left(&gb) < data_length) + goto too_short; + if (cc_data) + bytestream2_get_bufferu(&gb, cc_data, data_length); + return cc_count; + } + return AVERROR(EAGAIN); + +too_short: + av_log(log_ctx, AV_LOG_ERROR, "not enough bytes in cdp\n"); + return AVERROR_INVALIDDATA; +} diff --git a/libavcodec/smpte_436m.h b/libavcodec/smpte_436m.h new file mode 100644 index 0000000000..9c0e2a5a05 --- /dev/null +++ b/libavcodec/smpte_436m.h @@ -0,0 +1,254 @@ +/* + * MXF SMPTE-436M VBI/ANC parsing functions + * Copyright (c) 2025 Jacob Lifshay + * + * 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 + */ + +#ifndef AVCODEC_SMPTE_436M_H +#define AVCODEC_SMPTE_436M_H + +#include <stdint.h> + +/** + * Iterator over the ANC packets in a single AV_CODEC_ID_SMPTE_436M_ANC AVPacket's data + */ +typedef struct AVSmpte436mAncIterator { + uint16_t anc_packets_left; + int size_left; + const uint8_t *data_left; +} AVSmpte436mAncIterator; + +/** + * Wrapping Type from Table 7 (page 13) of: + * https://pub.smpte.org/latest/st436/s436m-2006.pdf + */ +typedef enum AVSmpte436mWrappingType +{ + AV_SMPTE_436M_WRAPPING_TYPE_VANC_FRAME = 1, + AV_SMPTE_436M_WRAPPING_TYPE_VANC_FIELD_1 = 2, + AV_SMPTE_436M_WRAPPING_TYPE_VANC_FIELD_2 = 3, + AV_SMPTE_436M_WRAPPING_TYPE_VANC_PROGRESSIVE_FRAME = 4, + AV_SMPTE_436M_WRAPPING_TYPE_HANC_FRAME = 0x11, + AV_SMPTE_436M_WRAPPING_TYPE_HANC_FIELD_1 = 0x12, + AV_SMPTE_436M_WRAPPING_TYPE_HANC_FIELD_2 = 0x13, + AV_SMPTE_436M_WRAPPING_TYPE_HANC_PROGRESSIVE_FRAME = 0x14, + /** not a real wrapping type, just here to guarantee the enum is big enough */ + AV_SMPTE_436M_WRAPPING_TYPE_MAX = 0xFF, +} AVSmpte436mWrappingType; + +/** + * Payload Sample Coding from Table 4 (page 10) and Table 7 (page 13) of: + * https://pub.smpte.org/latest/st436/s436m-2006.pdf + */ +typedef enum AVSmpte436mPayloadSampleCoding +{ + /** only used for VBI */ + AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_1BIT_LUMA = 1, + /** only used for VBI */ + AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_1BIT_COLOR_DIFF = 2, + /** only used for VBI */ + AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_1BIT_LUMA_AND_COLOR_DIFF = 3, + /** used for VBI and ANC */ + AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA = 4, + /** used for VBI and ANC */ + AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_COLOR_DIFF = 5, + /** used for VBI and ANC */ + AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA_AND_COLOR_DIFF = 6, + /** used for VBI and ANC */ + AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_10BIT_LUMA = 7, + /** used for VBI and ANC */ + AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_10BIT_COLOR_DIFF = 8, + /** used for VBI and ANC */ + AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_10BIT_LUMA_AND_COLOR_DIFF = 9, + /** only used for ANC */ + AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA_WITH_PARITY_ERROR = 10, + /** only used for ANC */ + AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_COLOR_DIFF_WITH_PARITY_ERROR = 11, + /** only used for ANC */ + AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA_AND_COLOR_DIFF_WITH_PARITY_ERROR = 12, + /** not a real sample coding, just here to guarantee the enum is big enough */ + AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_MAX = 0xFF, +} AVSmpte436mPayloadSampleCoding; + +/** the payload capacity of AVSmpte291mAnc8bit (and of AVSmpte291mAnc10bit when that gets added) */ +#define AV_SMPTE_291M_ANC_PAYLOAD_CAPACITY 0xFF + +/** + * An ANC packet with an 8-bit payload. + * This can be decoded from AVSmpte436mCodedAnc::payload. + * + * Note: Some ANC packets need a 10-bit payload, if stored in this struct, + * the most-significant 2 bits of each sample are discarded. + */ +typedef struct AVSmpte291mAnc8bit { + uint8_t did; + uint8_t sdid_or_dbn; + uint8_t data_count; + uint8_t payload[AV_SMPTE_291M_ANC_PAYLOAD_CAPACITY]; + uint8_t checksum; +} AVSmpte291mAnc8bit; + +/** max number of samples that can be stored in the payload of AVSmpte436mCodedAnc */ +#define AV_SMPTE_436M_CODED_ANC_SAMPLE_CAPACITY \ + (AV_SMPTE_291M_ANC_PAYLOAD_CAPACITY + 4) /* 4 for did, sdid_or_dbn, data_count, and checksum */ +/** max number of bytes that can be stored in the payload of AVSmpte436mCodedAnc */ +#define AV_SMPTE_436M_CODED_ANC_PAYLOAD_CAPACITY (((AV_SMPTE_436M_CODED_ANC_SAMPLE_CAPACITY + 2) / 3) * 4) + +/** + * An encoded ANC packet within a single AV_CODEC_ID_SMPTE_436M_ANC AVPacket's data. + * The repeated section of Table 7 (page 13) of: + * https://pub.smpte.org/latest/st436/s436m-2006.pdf + */ +typedef struct AVSmpte436mCodedAnc { + uint16_t line_number; + AVSmpte436mWrappingType wrapping_type; + AVSmpte436mPayloadSampleCoding payload_sample_coding; + uint16_t payload_sample_count; + uint32_t payload_array_length; + /** the payload, has size payload_array_length. + * can be decoded into AVSmpte291mAnc8bit + */ + uint8_t payload[AV_SMPTE_436M_CODED_ANC_PAYLOAD_CAPACITY]; +} AVSmpte436mCodedAnc; + +/** + * Validate a AVSmpte436mCodedAnc structure. Doesn't check if the payload is valid. + * @param[in] anc ANC packet to validate + * @return 0 on success, AVERROR codes otherwise. + */ +int av_smpte_436m_coded_anc_validate(const AVSmpte436mCodedAnc *anc); + +/** + * Encode ANC packets into a single AV_CODEC_ID_SMPTE_436M_ANC AVPacket's data. + * @param[in] anc_packet_count number of ANC packets to encode + * @param[in] anc_packets the ANC packets to encode + * @param[in] size the size of out. ignored if out is NULL. + * @param[out] out Output bytes. Doesn't write anything if out is NULL. + * @return the number of bytes written on success, AVERROR codes otherwise. + * If out is NULL, returns the number of bytes it would have written. + */ +int av_smpte_436m_anc_encode(uint8_t *out, int size, int anc_packet_count, const AVSmpte436mCodedAnc *anc_packets); + +struct AVPacket; + +/** + * Append more ANC packets to a single AV_CODEC_ID_SMPTE_436M_ANC AVPacket's data. + * @param[in] anc_packet_count number of ANC packets to encode + * @param[in] anc_packets the ANC packets to encode + * @param pkt the AVPacket to append to. + * it must either be size 0 or contain valid SMPTE_436M_ANC data. + * @return 0 on success, AVERROR codes otherwise. + */ +int av_smpte_436m_anc_append(struct AVPacket *pkt, int anc_packet_count, const AVSmpte436mCodedAnc *anc_packets); + +/** + * Set up iteration over the ANC packets in a single AV_CODEC_ID_SMPTE_436M_ANC AVPacket's data. + * @param[in] buf Pointer to the data from a AV_CODEC_ID_SMPTE_436M_ANC AVPacket. + * @param[in] buf_size Size of the data from a AV_CODEC_ID_SMPTE_436M_ANC AVPacket. + * @param[out] iter Pointer to the iterator. + * @return 0 on success, AVERROR codes otherwise. + */ +int av_smpte_436m_anc_iter_init(AVSmpte436mAncIterator *iter, const uint8_t *buf, int buf_size); + +/** + * Get the next ANC packet from the iterator, advancing the iterator. + * @param[in,out] iter Pointer to the iterator. + * @param[out] anc The returned ANC packet. + * @return 0 on success, AVERROR_EOF when the iterator has reached the end, AVERROR codes otherwise. + */ +int av_smpte_436m_anc_iter_next(AVSmpte436mAncIterator *iter, AVSmpte436mCodedAnc *anc); + +/** + * Get the minimum number of bytes needed to store a AVSmpte436mCodedAnc payload. + * @param sample_coding the payload sample coding + * @param sample_count the number of samples stored in the payload + * @return returns the minimum number of bytes needed, on error returns < 0. + * always <= SMPTE_436M_CODED_ANC_PAYLOAD_CAPACITY + */ +int av_smpte_436m_coded_anc_payload_size(AVSmpte436mPayloadSampleCoding sample_coding, uint16_t sample_count); + +/** + * Decode a AVSmpte436mCodedAnc payload into AVSmpte291mAnc8bit + * @param[in] sample_coding the payload sample coding + * @param[in] sample_count the number of samples stored in the payload + * @param[in] payload the bytes storing the payload, + * the needed size can be obtained from + avpriv_smpte_436m_coded_anc_payload_size + * @param[in] log_ctx context pointer for av_log + * @param[out] out The decoded ANC packet. + * @return returns 0 on success, otherwise < 0. + */ +int av_smpte_291m_anc_8bit_decode(AVSmpte291mAnc8bit *out, + AVSmpte436mPayloadSampleCoding sample_coding, + uint16_t sample_count, + const uint8_t *payload, + void *log_ctx); + +/** + * Fill in the correct checksum for a AVSmpte291mAnc8bit + * @param[in,out] anc The ANC packet. + */ +void av_smpte_291m_anc_8bit_fill_checksum(AVSmpte291mAnc8bit *anc); + +/** + * Compute the sample count needed to encode a AVSmpte291mAnc8bit into a AVSmpte436mCodedAnc payload + * @param[in] anc The ANC packet. + * @param[in] sample_coding The sample coding. + * @param[in] log_ctx context pointer for av_log + * @return returns the sample count on success, otherwise < 0. + */ +int av_smpte_291m_anc_8bit_get_sample_count(const AVSmpte291mAnc8bit *anc, + AVSmpte436mPayloadSampleCoding sample_coding, + void *log_ctx); + +/** + * Encode a AVSmpte291mAnc8bit into a AVSmpte436mCodedAnc + * @param[in] line_number the line number the ANC packet is on + * @param[in] wrapping_type the wrapping type + * @param[in] sample_coding the payload sample coding + * @param[in] payload the ANC packet to encode. + * @param[in] log_ctx context pointer for av_log + * @param[out] out The encoded ANC packet. + * @return returns 0 on success, otherwise < 0. + */ +int av_smpte_291m_anc_8bit_encode(AVSmpte436mCodedAnc *out, + uint16_t line_number, + AVSmpte436mWrappingType wrapping_type, + AVSmpte436mPayloadSampleCoding sample_coding, + const AVSmpte291mAnc8bit *payload, + void *log_ctx); + +/** AVSmpte291mAnc8bit::did when carrying CTA-708 data (for AV_CODEC_ID_EIA_608) */ +#define AV_SMPTE_291M_ANC_DID_CTA_708 0x61 + +/** AVSmpte291mAnc8bit::sdid_or_dbn when carrying CTA-708 data (for AV_CODEC_ID_EIA_608) */ +#define AV_SMPTE_291M_ANC_SDID_CTA_708 0x1 + +/** + * Try to decode an ANC packet into EIA-608/CTA-708 data (AV_CODEC_ID_EIA_608). This + * @param[in] anc The ANC packet. + * @param[in] log_ctx Context pointer for av_log + * @param[out] cc_data the buffer to store the extracted EIA-608/CTA-708 data, + * you can pass NULL to not store the data. + * the required size is 3 * cc_count bytes. + * SMPTE_291M_ANC_PAYLOAD_CAPACITY is always enough size. + * @return returns cc_count (>= 0) on success, AVERROR(EAGAIN) if it wasn't a CTA-708 ANC packet, < 0 on error. + */ +int av_smpte_291m_anc_8bit_extract_cta_708(const AVSmpte291mAnc8bit *anc, uint8_t *cc_data, void *log_ctx); + +#endif /* AVCODEC_SMPTE_436M_H */ diff --git a/libavcodec/smpte_436m_internal.h b/libavcodec/smpte_436m_internal.h new file mode 100644 index 0000000000..7481949fb6 --- /dev/null +++ b/libavcodec/smpte_436m_internal.h @@ -0,0 +1,98 @@ +/* + * MXF SMPTE-436M VBI/ANC internals + * Copyright (c) 2025 Jacob Lifshay + * + * 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 + */ + +#ifndef AVCODEC_SMPTE_436M_INTERNAL_H +#define AVCODEC_SMPTE_436M_INTERNAL_H + +#include "smpte_436m.h" + +// clang-format off +#define FF_SMPTE_436M_WRAPPING_TYPE_VANC_AVOPTIONS(flags, unit_name) \ + { "vanc_frame", "VANC frame (interlaced or segmented progressive frame)", 0, AV_OPT_TYPE_CONST, \ + {.i64 = AV_SMPTE_436M_WRAPPING_TYPE_VANC_FRAME}, 0, 0xFF, flags, .unit = unit_name }, \ + { "vanc_field_1", "VANC field 1", 0, AV_OPT_TYPE_CONST, \ + {.i64 = AV_SMPTE_436M_WRAPPING_TYPE_VANC_FIELD_1}, 0, 0xFF, flags, .unit = unit_name }, \ + { "vanc_field_2", "VANC field 2", 0, AV_OPT_TYPE_CONST, \ + {.i64 = AV_SMPTE_436M_WRAPPING_TYPE_VANC_FIELD_2}, 0, 0xFF, flags, .unit = unit_name }, \ + { "vanc_progressive_frame", "VANC progressive frame", 0, AV_OPT_TYPE_CONST, \ + {.i64 = AV_SMPTE_436M_WRAPPING_TYPE_VANC_PROGRESSIVE_FRAME}, 0, 0xFF, flags, .unit = unit_name } + +#define FF_SMPTE_436M_WRAPPING_TYPE_HANC_AVOPTIONS(flags, unit_name) \ + { "hanc_frame", "HANC frame (interlaced or segmented progressive frame)", 0, AV_OPT_TYPE_CONST, \ + {.i64 = AV_SMPTE_436M_WRAPPING_TYPE_HANC_FRAME}, 0, 0xFF, flags, .unit = unit_name }, \ + { "hanc_field_1", "HANC field 1", 0, AV_OPT_TYPE_CONST, \ + {.i64 = AV_SMPTE_436M_WRAPPING_TYPE_HANC_FIELD_1}, 0, 0xFF, flags, .unit = unit_name }, \ + { "hanc_field_2", "HANC field 2", 0, AV_OPT_TYPE_CONST, \ + {.i64 = AV_SMPTE_436M_WRAPPING_TYPE_HANC_FIELD_2}, 0, 0xFF, flags, .unit = unit_name }, \ + { "hanc_progressive_frame", "HANC progressive frame", 0, AV_OPT_TYPE_CONST, \ + {.i64 = AV_SMPTE_436M_WRAPPING_TYPE_HANC_PROGRESSIVE_FRAME}, 0, 0xFF, flags, .unit = unit_name } + +#define FF_SMPTE_436M_WRAPPING_TYPE_AVOPTIONS(flags, unit_name) \ + FF_SMPTE_436M_WRAPPING_TYPE_VANC_AVOPTIONS(flags, unit_name), \ + FF_SMPTE_436M_WRAPPING_TYPE_HANC_AVOPTIONS(flags, unit_name) +// clang-format on + +// clang-format off +#define FF_SMPTE_436M_PAYLOAD_SAMPLE_CODING_ONLY_IN_VBI_AVOPTIONS(flags, unit_name) \ + { "1bit_luma", "1-bit component luma samples", 0, AV_OPT_TYPE_CONST, \ + {.i64 = AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_1BIT_LUMA}, 0, 0xFF, flags, .unit = unit_name }, \ + { "1bit_color_diff", "1-bit component color difference samples", 0, AV_OPT_TYPE_CONST, \ + {.i64 = AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_1BIT_COLOR_DIFF}, 0, 0xFF, flags, .unit = unit_name }, \ + { "1bit_luma_and_color_diff", "1-bit component luma and color difference samples", 0, AV_OPT_TYPE_CONST, \ + {.i64 = AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_1BIT_LUMA_AND_COLOR_DIFF}, 0, 0xFF, flags, .unit = unit_name } + +#define FF_SMPTE_436M_PAYLOAD_SAMPLE_CODING_SHARED_AVOPTIONS(flags, unit_name) \ + { "8bit_luma", "8-bit component luma samples", 0, AV_OPT_TYPE_CONST, \ + {.i64 = AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA}, 0, 0xFF, flags, .unit = unit_name }, \ + { "8bit_color_diff", "8-bit component color difference samples", 0, AV_OPT_TYPE_CONST, \ + {.i64 = AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_COLOR_DIFF}, 0, 0xFF, flags, .unit = unit_name }, \ + { "8bit_luma_and_color_diff", "8-bit component luma and color difference samples", 0, AV_OPT_TYPE_CONST, \ + {.i64 = AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA_AND_COLOR_DIFF}, 0, 0xFF, flags, .unit = unit_name }, \ + { "10bit_luma", "10-bit component luma samples", 0, AV_OPT_TYPE_CONST, \ + {.i64 = AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_10BIT_LUMA}, 0, 0xFF, flags, .unit = unit_name }, \ + { "10bit_color_diff", "10-bit component color difference samples", 0, AV_OPT_TYPE_CONST, \ + {.i64 = AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_10BIT_COLOR_DIFF}, 0, 0xFF, flags, .unit = unit_name }, \ + { "10bit_luma_and_color_diff", "10-bit component luma and color difference samples", 0, AV_OPT_TYPE_CONST, \ + {.i64 = AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_10BIT_LUMA_AND_COLOR_DIFF}, 0, 0xFF, flags, .unit = unit_name } + +#define FF_SMPTE_436M_PAYLOAD_SAMPLE_CODING_ONLY_IN_ANC_AVOPTIONS(flags, unit_name) \ + { "8bit_luma_parity_error", "8-bit component luma samples with parity error", 0, AV_OPT_TYPE_CONST, \ + {.i64 = AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA_WITH_PARITY_ERROR}, 0, 0xFF, flags, .unit = unit_name }, \ + { "8bit_color_diff_parity_error", "8-bit component color difference samples with parity error", 0, AV_OPT_TYPE_CONST, \ + {.i64 = AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_COLOR_DIFF_WITH_PARITY_ERROR}, 0, 0xFF, flags, .unit = unit_name }, \ + { "8bit_luma_and_color_diff_parity_error", "8-bit component luma and color difference samples with parity error", 0, AV_OPT_TYPE_CONST, \ + {.i64 = AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA_AND_COLOR_DIFF_WITH_PARITY_ERROR}, 0, 0xFF, flags, .unit = unit_name } + +#define FF_SMPTE_436M_PAYLOAD_SAMPLE_CODING_VBI_AVOPTIONS(flags, unit_name) \ + FF_SMPTE_436M_PAYLOAD_SAMPLE_CODING_ONLY_IN_VBI_AVOPTIONS(flags, unit_name), \ + FF_SMPTE_436M_PAYLOAD_SAMPLE_CODING_SHARED_AVOPTIONS(flags, unit_name) + +#define FF_SMPTE_436M_PAYLOAD_SAMPLE_CODING_ANC_AVOPTIONS(flags, unit_name) \ + FF_SMPTE_436M_PAYLOAD_SAMPLE_CODING_SHARED_AVOPTIONS(flags, unit_name), \ + FF_SMPTE_436M_PAYLOAD_SAMPLE_CODING_ONLY_IN_ANC_AVOPTIONS(flags, unit_name) + +#define FF_SMPTE_436M_PAYLOAD_SAMPLE_CODING_AVOPTIONS(flags, unit_name) \ + FF_SMPTE_436M_PAYLOAD_SAMPLE_CODING_ONLY_IN_VBI_AVOPTIONS(flags, unit_name), \ + FF_SMPTE_436M_PAYLOAD_SAMPLE_CODING_SHARED_AVOPTIONS(flags, unit_name), \ + FF_SMPTE_436M_PAYLOAD_SAMPLE_CODING_ONLY_IN_ANC_AVOPTIONS(flags, unit_name) +// clang-format on + +#endif /* AVCODEC_SMPTE_436M_INTERNAL_H */ |