History

lavc: add eia608_to_smpte436m bitstream filter

Jacob Lifshay committed 1 year ago

Commit 0271d041 parent aa5b9db5 files · +326 −0Browse files

Unified · Split

5 files
  1. configure +1 −0
  2. doc/bitstream_filters.texi +46 −0
  3. libavcodec/bitstream_filters.c +1 −0
  4. libavcodec/bsf/Makefile +1 −0
  5. libavcodec/bsf/eia608_to_smpte436m.c +277 −0
configure+1−0
@@ -3531,6 +3531,7 @@ av1_metadata_bsf_select="cbs_av1" · 3530 unmodified lines
3531dovi_rpu_bsf_select="cbs_h265 cbs_av1 dovi_rpudec dovi_rpuenc"3531dovi_rpu_bsf_select="cbs_h265 cbs_av1 dovi_rpudec dovi_rpuenc"
3532dts2pts_bsf_select="cbs_h264 h264parse"3532dts2pts_bsf_select="cbs_h264 h264parse"
3533eac3_core_bsf_select="ac3_parser"3533eac3_core_bsf_select="ac3_parser"
3534eia608_to_smpte436m_bsf_select="smpte_436m"
3534evc_frame_merge_bsf_select="evcparse"3535evc_frame_merge_bsf_select="evcparse"
3535filter_units_bsf_select="cbs"3536filter_units_bsf_select="cbs"
3536h264_metadata_bsf_deps="const_nan"3537h264_metadata_bsf_deps="const_nan"
doc/bitstream_filters.texi+46−0
@@ -189,6 +189,52 @@ see page 44-46 or section 5.5 of · 188 unmodified lines
189 189
190Extract the core from a E-AC-3 stream, dropping extra channels.190Extract the core from a E-AC-3 stream, dropping extra channels.
191 191
192@section eia608_to_smpte436m
193
194Convert from a @code{EIA_608} stream to a @code{SMPTE_436M_ANC} data stream, wrapping the closed captions in CTA-708 CDP VANC packets.
195
196@table @option
197@item line_number
198Choose which line number the generated VANC packets should go on. You generally want either line 9 (the default) or 11.
199@item wrapping_type
200Choose the SMPTE 436M wrapping type, defaults to @samp{vanc_frame}.
201It accepts the values:
202@table @samp
203@item vanc_frame
204VANC frame (interlaced or segmented progressive frame)
205@item vanc_field_1
206@item vanc_field_2
207@item vanc_progressive_frame
208@end table
209@item sample_coding
210Choose the SMPTE 436M sample coding, defaults to @samp{8bit_luma}.
211It accepts the values:
212@table @samp
213@item 8bit_luma
2148-bit component luma samples
215@item 8bit_color_diff
2168-bit component color difference samples
217@item 8bit_luma_and_color_diff
2188-bit component luma and color difference samples
219@item 10bit_luma
22010-bit component luma samples
221@item 10bit_color_diff
22210-bit component color difference samples
223@item 10bit_luma_and_color_diff
22410-bit component luma and color difference samples
225@item 8bit_luma_parity_error
2268-bit component luma samples with parity error
227@item 8bit_color_diff_parity_error
2288-bit component color difference samples with parity error
229@item 8bit_luma_and_color_diff_parity_error
2308-bit component luma and color difference samples with parity error
231@end table
232@item initial_cdp_sequence_cntr
233The initial value of the CDP's 16-bit unsigned integer @code{cdp_hdr_sequence_cntr} and @code{cdp_ftr_sequence_cntr} fields. Defaults to 0.
234@item cdp_frame_rate
235Set the CDP's @code{cdp_frame_rate} field. This doesn't actually change the timing of the data stream, it just changes the values inserted in that field in the generated CDP packets. Defaults to @samp{30000/1001}.
236@end table
237
192@section extract_extradata238@section extract_extradata
193 239
194Extract the in-band extradata.240Extract the in-band extradata.
libavcodec/bitstream_filters.c+1−0
@@ -36,6 +36,7 @@ extern const FFBitStreamFilter ff_dovi_rpu_bsf; · 35 unmodified lines
36extern const FFBitStreamFilter ff_dts2pts_bsf;36extern const FFBitStreamFilter ff_dts2pts_bsf;
37extern const FFBitStreamFilter ff_dv_error_marker_bsf;37extern const FFBitStreamFilter ff_dv_error_marker_bsf;
38extern const FFBitStreamFilter ff_eac3_core_bsf;38extern const FFBitStreamFilter ff_eac3_core_bsf;
39extern const FFBitStreamFilter ff_eia608_to_smpte436m_bsf;
39extern const FFBitStreamFilter ff_evc_frame_merge_bsf;40extern const FFBitStreamFilter ff_evc_frame_merge_bsf;
40extern const FFBitStreamFilter ff_extract_extradata_bsf;41extern const FFBitStreamFilter ff_extract_extradata_bsf;
41extern const FFBitStreamFilter ff_filter_units_bsf;42extern const FFBitStreamFilter ff_filter_units_bsf;
libavcodec/bsf/Makefile+1−0
@@ -12,6 +12,7 @@ OBJS-$(CONFIG_DTS2PTS_BSF) += bsf/dts2pts.o · 11 unmodified lines
12OBJS-$(CONFIG_DUMP_EXTRADATA_BSF) += bsf/dump_extradata.o12OBJS-$(CONFIG_DUMP_EXTRADATA_BSF) += bsf/dump_extradata.o
13OBJS-$(CONFIG_DV_ERROR_MARKER_BSF) += bsf/dv_error_marker.o13OBJS-$(CONFIG_DV_ERROR_MARKER_BSF) += bsf/dv_error_marker.o
14OBJS-$(CONFIG_EAC3_CORE_BSF) += bsf/eac3_core.o14OBJS-$(CONFIG_EAC3_CORE_BSF) += bsf/eac3_core.o
15OBJS-$(CONFIG_EIA608_TO_SMPTE436M_BSF) += bsf/eia608_to_smpte436m.o
15OBJS-$(CONFIG_EVC_FRAME_MERGE_BSF) += bsf/evc_frame_merge.o16OBJS-$(CONFIG_EVC_FRAME_MERGE_BSF) += bsf/evc_frame_merge.o
16OBJS-$(CONFIG_EXTRACT_EXTRADATA_BSF) += bsf/extract_extradata.o17OBJS-$(CONFIG_EXTRACT_EXTRADATA_BSF) += bsf/extract_extradata.o
17OBJS-$(CONFIG_FILTER_UNITS_BSF) += bsf/filter_units.o18OBJS-$(CONFIG_FILTER_UNITS_BSF) += bsf/filter_units.o
libavcodec/bsf/eia608_to_smpte436m.c+277−0
@@ -0,0 +1,277 @@
1/*
2 * EIA-608 to MXF SMPTE-436M ANC bitstream filter
3 * Copyright (c) 2025 Jacob Lifshay
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#include "bsf.h"
23#include "bsf_internal.h"
24#include "codec_id.h"
25#include "libavcodec/smpte_436m.h"
26#include "libavcodec/smpte_436m_internal.h"
27#include "libavutil/avassert.h"
28#include "libavutil/avutil.h"
29#include "libavutil/error.h"
30#include "libavutil/intreadwrite.h"
31#include "libavutil/macros.h"
32#include "libavutil/opt.h"
33#include "libavutil/rational.h"
34
35typedef struct EIA608ToSMPTE436MContext {
36 const AVClass *class;
37 unsigned line_number;
38 unsigned cdp_sequence_cntr;
39 unsigned wrapping_type_opt;
40 unsigned sample_coding_opt;
41 AVSmpte436mWrappingType wrapping_type;
42 AVSmpte436mPayloadSampleCoding sample_coding;
43 AVRational cdp_frame_rate;
44 uint8_t cdp_frame_rate_byte;
45} EIA608ToSMPTE436MContext;
46
47// clang-format off
48static const AVSmpte291mAnc8bit test_anc = {
49 .did = 0x61,
50 .sdid_or_dbn = 0x01,
51 .data_count = 0x49,
52 .payload = {
53 // header
54 0x96, 0x69, 0x49, 0x7F, 0x43, 0xFA, 0x8D, 0x72, 0xF4,
55
56 // 608 triples
57 0xFC, 0x80, 0x80, 0xFD, 0x80, 0x80,
58
59 // 708 padding
60 0xFA, 0x00, 0x00, 0xFA, 0x00, 0x00, 0xFA, 0x00, 0x00,
61 0xFA, 0x00, 0x00, 0xFA, 0x00, 0x00, 0xFA, 0x00, 0x00,
62 0xFA, 0x00, 0x00, 0xFA, 0x00, 0x00, 0xFA, 0x00, 0x00,
63 0xFA, 0x00, 0x00, 0xFA, 0x00, 0x00, 0xFA, 0x00, 0x00,
64 0xFA, 0x00, 0x00, 0xFA, 0x00, 0x00, 0xFA, 0x00, 0x00,
65 0xFA, 0x00, 0x00, 0xFA, 0x00, 0x00, 0xFA, 0x00, 0x00,
66
67 // footer
68 0x74, 0xFA, 0x8D, 0x81,
69 },
70 .checksum = 0xAB,
71};
72// clang-format on
73
74static av_cold int ff_eia608_to_smpte436m_init(AVBSFContext *ctx)
75{
76 EIA608ToSMPTE436MContext *priv = ctx->priv_data;
77
78 priv->wrapping_type = priv->wrapping_type_opt;
79 priv->sample_coding = priv->sample_coding_opt;
80
81 // validate we can handle the selected wrapping type and sample coding
82
83 AVSmpte436mCodedAnc coded_anc;
84
85 int ret = av_smpte_291m_anc_8bit_encode(
86 &coded_anc, priv->line_number, priv->wrapping_type, priv->sample_coding, &test_anc, ctx);
87 if (ret < 0)
88 return ret;
89
90 ctx->par_out->codec_type = AVMEDIA_TYPE_DATA;
91 ctx->par_out->codec_id = AV_CODEC_ID_SMPTE_436M_ANC;
92
93 static const struct {
94 AVRational frame_rate;
95 uint8_t cdp_frame_rate;
96 } known_frame_rates[] = {
97 { .frame_rate = { .num = 24000, .den = 1001 }, .cdp_frame_rate = 0x1F },
98 { .frame_rate = { .num = 24, .den = 1 }, .cdp_frame_rate = 0x2F },
99 { .frame_rate = { .num = 25, .den = 1 }, .cdp_frame_rate = 0x3F },
100 { .frame_rate = { .num = 30000, .den = 1001 }, .cdp_frame_rate = 0x4F },
101 { .frame_rate = { .num = 30, .den = 1 }, .cdp_frame_rate = 0x5F },
102 { .frame_rate = { .num = 50, .den = 1 }, .cdp_frame_rate = 0x6F },
103 { .frame_rate = { .num = 60000, .den = 1001 }, .cdp_frame_rate = 0x7F },
104 { .frame_rate = { .num = 60, .den = 1 }, .cdp_frame_rate = 0x8F },
105 };
106
107 priv->cdp_frame_rate_byte = 0;
108
109 for (int i = 0; i < FF_ARRAY_ELEMS(known_frame_rates); i++) {
110 if (known_frame_rates[i].frame_rate.num == priv->cdp_frame_rate.num && known_frame_rates[i].frame_rate.den == priv->cdp_frame_rate.den) {
111 priv->cdp_frame_rate_byte = known_frame_rates[i].cdp_frame_rate;
112 break;
113 }
114 }
115
116 if (priv->cdp_frame_rate_byte == 0) {
117 av_log(ctx,
118 AV_LOG_FATAL,
119 "cdp_frame_rate not supported: %d/%d\n",
120 priv->cdp_frame_rate.num,
121 priv->cdp_frame_rate.den);
122 return AVERROR(EINVAL);
123 }
124
125 return 0;
126}
127
128static int ff_eia608_to_smpte436m_filter(AVBSFContext *ctx, AVPacket *out)
129{
130 EIA608ToSMPTE436MContext *priv = ctx->priv_data;
131 AVPacket *in;
132
133 int ret = ff_bsf_get_packet(ctx, &in);
134 if (ret < 0)
135 return ret;
136
137 AVSmpte291mAnc8bit anc;
138 anc.did = 0x61;
139 anc.sdid_or_dbn = 0x1;
140
141 uint8_t *p = anc.payload;
142
143 *p++ = 0x96; // cdp_identifier -- always 0x9669
144 *p++ = 0x69;
145
146 uint8_t *cdp_length_p = p++;
147
148 *p++ = priv->cdp_frame_rate_byte;
149
150 const uint8_t FLAG_CC_DATA_PRESENT = 0x40;
151 const uint8_t FLAG_CAPTION_SERVICE_ACTIVE = 0x2;
152 const uint8_t FLAG_RESERVED = 0x1; // must always be set
153
154 *p++ = FLAG_CC_DATA_PRESENT | FLAG_CAPTION_SERVICE_ACTIVE | FLAG_RESERVED;
155
156 AV_WB16(p, priv->cdp_sequence_cntr);
157 p += 2;
158
159 const uint8_t CC_DATA_SECTION_ID = 0x72;
160
161 *p++ = CC_DATA_SECTION_ID;
162
163 uint8_t *cc_count_p = p++;
164
165 const uint8_t CC_COUNT_MASK = 0x1F;
166 const int CDP_FOOTER_SIZE = 4;
167
168 int cc_count = in->size / 3;
169 int space_left = AV_SMPTE_291M_ANC_PAYLOAD_CAPACITY - (p - anc.payload);
170 int cc_data_space_left = space_left - CDP_FOOTER_SIZE;
171 int max_cc_count = FFMAX(cc_data_space_left / 3, CC_COUNT_MASK);
172
173 if (cc_count > max_cc_count) {
174 av_log(ctx,
175 AV_LOG_ERROR,
176 "cc_count (%d) is bigger than the maximum supported (%d), truncating captions packet\n",
177 cc_count,
178 max_cc_count);
179 cc_count = max_cc_count;
180 }
181
182 *cc_count_p = cc_count | ~CC_COUNT_MASK; // other bits are reserved and set to ones
183
184 for (size_t i = 0; i < cc_count; i++) {
185 size_t start = i * 3;
186 *p++ = in->data[start] | 0xF8; // fill reserved bits with ones
187 *p++ = in->data[start + 1];
188 *p++ = in->data[start + 2];
189 }
190
191 const uint8_t CDP_FOOTER_ID = 0x74;
192
193 *p++ = CDP_FOOTER_ID;
194
195 AV_WB16(p, priv->cdp_sequence_cntr);
196 p += 2;
197
198 uint8_t *packet_checksum_p = p;
199 *p++ = 0;
200
201 anc.data_count = p - anc.payload;
202 *cdp_length_p = anc.data_count;
203
204 int sum = 0;
205 for (int i = 0; i < anc.data_count; i++) {
206 sum += anc.payload[i];
207 }
208 // set to an 8-bit value such that the sum of the bytes of the whole CDP mod 2^8 is 0
209 *packet_checksum_p = -sum;
210
211 priv->cdp_sequence_cntr++;
212 // cdp_sequence_cntr wraps around at 16-bits
213 priv->cdp_sequence_cntr &= 0xFFFFU;
214
215 av_smpte_291m_anc_8bit_fill_checksum(&anc);
216
217 AVSmpte436mCodedAnc coded_anc;
218 ret = av_smpte_291m_anc_8bit_encode(
219 &coded_anc, priv->line_number, (AVSmpte436mWrappingType)priv->wrapping_type, priv->sample_coding, &anc, ctx);
220 if (ret < 0)
221 goto fail;
222
223 ret = av_smpte_436m_anc_encode(NULL, 0, 1, &coded_anc);
224 if (ret < 0)
225 goto fail;
226
227 ret = av_new_packet(out, ret);
228 if (ret < 0)
229 goto fail;
230
231 ret = av_packet_copy_props(out, in);
232 if (ret < 0)
233 goto fail;
234
235 ret = av_smpte_436m_anc_encode(out->data, out->size, 1, &coded_anc);
236 if (ret < 0)
237 goto fail;
238
239 return 0;
240
241fail:
242 if (ret < 0)
243 av_packet_unref(out);
244 av_packet_free(&in);
245 return ret;
246}
247
248#define OFFSET(x) offsetof(EIA608ToSMPTE436MContext, x)
249#define FLAGS AV_OPT_FLAG_BSF_PARAM
250// clang-format off
251static const AVOption options[] = {
252 { "line_number", "line number -- you probably want 9 or 11", OFFSET(line_number), AV_OPT_TYPE_UINT, { .i64 = 9 }, 0, 0xFFFF, FLAGS },
253 { "wrapping_type", "wrapping type", OFFSET(wrapping_type_opt), AV_OPT_TYPE_UINT, { .i64 = AV_SMPTE_436M_WRAPPING_TYPE_VANC_FRAME }, 0, 0xFF, FLAGS, .unit = "wrapping_type" },
254 FF_SMPTE_436M_WRAPPING_TYPE_VANC_AVOPTIONS(FLAGS, "wrapping_type"),
255 { "sample_coding", "payload sample coding", OFFSET(sample_coding_opt), AV_OPT_TYPE_UINT, { .i64 = AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA }, 0, 0xFF, FLAGS, .unit = "sample_coding" },
256 FF_SMPTE_436M_PAYLOAD_SAMPLE_CODING_ANC_AVOPTIONS(FLAGS, "sample_coding"),
257 { "initial_cdp_sequence_cntr", "initial cdp_*_sequence_cntr value", OFFSET(cdp_sequence_cntr), AV_OPT_TYPE_UINT, { .i64 = 0 }, 0, 0xFFFF, FLAGS },
258 { "cdp_frame_rate", "set the `cdp_frame_rate` fields", OFFSET(cdp_frame_rate), AV_OPT_TYPE_VIDEO_RATE, { .str = "30000/1001" }, 0, INT_MAX, FLAGS },
259 { NULL },
260};
261// clang-format on
262
263static const AVClass eia608_to_smpte436m_class = {
264 .class_name = "eia608_to_smpte436m bitstream filter",
265 .item_name = av_default_item_name,
266 .option = options,
267 .version = LIBAVUTIL_VERSION_INT,
268};
269
270const FFBitStreamFilter ff_eia608_to_smpte436m_bsf = {
271 .p.name = "eia608_to_smpte436m",
272 .p.codec_ids = (const enum AVCodecID[]){ AV_CODEC_ID_EIA_608, AV_CODEC_ID_NONE },
273 .p.priv_class = &eia608_to_smpte436m_class,
274 .priv_data_size = sizeof(EIA608ToSMPTE436MContext),
275 .init = ff_eia608_to_smpte436m_init,
276 .filter = ff_eia608_to_smpte436m_filter,
277};

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t