From aa5b9db7bd55491f36af455ff98636af5a8143ec Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Tue, 22 Jul 2025 23:17:37 -0700 Subject: [PATCH] lavc: add smpte436m_to_eia608 bitstream filter Signed-off-by: Jacob Lifshay --- configure | 1 + doc/bitstream_filters.texi | 5 ++ libavcodec/bitstream_filters.c | 1 + libavcodec/bsf/Makefile | 1 + libavcodec/bsf/smpte436m_to_eia608.c | 89 ++++++++++++++++++++++++++++ 5 files changed, 97 insertions(+) create mode 100644 libavcodec/bsf/smpte436m_to_eia608.c diff --git a/configure b/configure index 01b8f596fd..828e6f1e77 100755 --- a/configure +++ b/configure @@ -3539,6 +3539,7 @@ h264_redundant_pps_bsf_select="cbs_h264" hevc_metadata_bsf_select="cbs_h265" mjpeg2jpeg_bsf_select="jpegtables" mpeg2_metadata_bsf_select="cbs_mpeg2" +smpte436m_to_eia608_bsf_select="smpte_436m" trace_headers_bsf_select="cbs cbs_vp8" vp9_metadata_bsf_select="cbs_vp9" vvc_metadata_bsf_select="cbs_h266" diff --git a/doc/bitstream_filters.texi b/doc/bitstream_filters.texi index 28152de6fa..b4f20fecd8 100644 --- a/doc/bitstream_filters.texi +++ b/doc/bitstream_filters.texi @@ -948,6 +948,11 @@ ffmpeg -i INPUT -c:a copy -bsf:a setts=pts=DTS out.mkv Log basic packet information. Mainly useful for testing, debugging, and development. +@section smpte436m_to_eia608 + +Convert from a @code{SMPTE_436M_ANC} data stream to a @code{EIA_608} stream, +extracting the closed captions from CTA-708 CDP VANC packets, and ignoring all other data. + @anchor{text2movsub} @section text2movsub diff --git a/libavcodec/bitstream_filters.c b/libavcodec/bitstream_filters.c index da9d0d2513..af5d34f8f6 100644 --- a/libavcodec/bitstream_filters.c +++ b/libavcodec/bitstream_filters.c @@ -61,6 +61,7 @@ extern const FFBitStreamFilter ff_prores_metadata_bsf; extern const FFBitStreamFilter ff_remove_extradata_bsf; extern const FFBitStreamFilter ff_setts_bsf; extern const FFBitStreamFilter ff_showinfo_bsf; +extern const FFBitStreamFilter ff_smpte436m_to_eia608_bsf; extern const FFBitStreamFilter ff_text2movsub_bsf; extern const FFBitStreamFilter ff_trace_headers_bsf; extern const FFBitStreamFilter ff_truehd_core_bsf; diff --git a/libavcodec/bsf/Makefile b/libavcodec/bsf/Makefile index 39ea091b50..476067c5df 100644 --- a/libavcodec/bsf/Makefile +++ b/libavcodec/bsf/Makefile @@ -38,6 +38,7 @@ OBJS-$(CONFIG_PRORES_METADATA_BSF) += bsf/prores_metadata.o OBJS-$(CONFIG_REMOVE_EXTRADATA_BSF) += bsf/remove_extradata.o OBJS-$(CONFIG_SETTS_BSF) += bsf/setts.o OBJS-$(CONFIG_SHOWINFO_BSF) += bsf/showinfo.o +OBJS-$(CONFIG_SMPTE436M_TO_EIA608_BSF) += bsf/smpte436m_to_eia608.o OBJS-$(CONFIG_TEXT2MOVSUB_BSF) += bsf/movsub.o OBJS-$(CONFIG_TRACE_HEADERS_BSF) += bsf/trace_headers.o OBJS-$(CONFIG_TRUEHD_CORE_BSF) += bsf/truehd_core.o diff --git a/libavcodec/bsf/smpte436m_to_eia608.c b/libavcodec/bsf/smpte436m_to_eia608.c new file mode 100644 index 0000000000..83a220cd1f --- /dev/null +++ b/libavcodec/bsf/smpte436m_to_eia608.c @@ -0,0 +1,89 @@ +/* + * MXF SMPTE-436M ANC to EIA-608 bitstream filter + * 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 "bsf.h" +#include "bsf_internal.h" +#include "codec_id.h" +#include "libavcodec/smpte_436m.h" +#include "libavutil/error.h" + +static av_cold int ff_smpte436m_to_eia608_init(AVBSFContext *ctx) +{ + ctx->par_out->codec_type = AVMEDIA_TYPE_SUBTITLE; + ctx->par_out->codec_id = AV_CODEC_ID_EIA_608; + return 0; +} + +static int ff_smpte436m_to_eia608_filter(AVBSFContext *ctx, AVPacket *out) +{ + AVPacket *in; + int ret = ff_bsf_get_packet(ctx, &in); + if (ret < 0) + return ret; + + AVSmpte436mAncIterator iter; + ret = av_smpte_436m_anc_iter_init(&iter, in->data, in->size); + if (ret < 0) + goto fail; + AVSmpte436mCodedAnc coded_anc; + while ((ret = av_smpte_436m_anc_iter_next(&iter, &coded_anc)) >= 0) { + AVSmpte291mAnc8bit anc; + ret = av_smpte_291m_anc_8bit_decode( + &anc, coded_anc.payload_sample_coding, coded_anc.payload_sample_count, coded_anc.payload, ctx); + if (ret < 0) + goto fail; + ret = av_smpte_291m_anc_8bit_extract_cta_708(&anc, NULL, ctx); + if (ret == AVERROR(EAGAIN)) + continue; + if (ret < 0) + goto fail; + int cc_count = ret; + + ret = av_new_packet(out, 3 * cc_count); + if (ret < 0) + goto fail; + + ret = av_packet_copy_props(out, in); + if (ret < 0) + goto fail; + + // verified it won't fail by running it above + av_smpte_291m_anc_8bit_extract_cta_708(&anc, out->data, ctx); + + return 0; + } + if (ret != AVERROR_EOF) + return ret; + ret = AVERROR(EAGAIN); + +fail: + if (ret < 0) + av_packet_unref(out); + av_packet_free(&in); + return ret; +} + +const FFBitStreamFilter ff_smpte436m_to_eia608_bsf = { + .p.name = "smpte436m_to_eia608", + .p.codec_ids = (const enum AVCodecID[]){ AV_CODEC_ID_SMPTE_436M_ANC, AV_CODEC_ID_NONE }, + .init = ff_smpte436m_to_eia608_init, + .filter = ff_smpte436m_to_eia608_filter, +};