FFmpeg
eia608_to_smpte436m.c
Go to the documentation of this file.
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"
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 
35 typedef struct EIA608ToSMPTE436MContext {
36  const AVClass *class;
37  unsigned line_number;
46 
47 // clang-format off
48 static 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 
75 {
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 
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 
129 {
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 
216 
217  AVSmpte436mCodedAnc coded_anc;
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 
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  ret = 0;
240 
241 fail:
242  if (ret < 0)
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
251 static 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" },
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" },
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 
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 
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),
277 };
FLAGS
#define FLAGS
Definition: eia608_to_smpte436m.c:249
AV_CODEC_ID_EIA_608
@ AV_CODEC_ID_EIA_608
Definition: codec_id.h:572
av_packet_unref
void av_packet_unref(AVPacket *pkt)
Wipe the packet.
Definition: packet.c:432
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
opt.h
bsf_internal.h
out
FILE * out
Definition: movenc.c:55
EIA608ToSMPTE436MContext::wrapping_type
AVSmpte436mWrappingType wrapping_type
Definition: eia608_to_smpte436m.c:41
options
static const AVOption options[]
Definition: eia608_to_smpte436m.c:251
AV_OPT_TYPE_VIDEO_RATE
@ AV_OPT_TYPE_VIDEO_RATE
Underlying C type is AVRational.
Definition: opt.h:315
rational.h
AVBitStreamFilter::name
const char * name
Definition: bsf.h:112
FLAG_RESERVED
@ FLAG_RESERVED
Definition: nut.h:50
AVPacket::data
uint8_t * data
Definition: packet.h:552
test_anc
static const AVSmpte291mAnc8bit test_anc
Definition: eia608_to_smpte436m.c:48
AVOption
AVOption.
Definition: opt.h:429
AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA
@ AV_SMPTE_436M_PAYLOAD_SAMPLE_CODING_8BIT_LUMA
used for VBI and ANC
Definition: smpte_436m.h:67
filter
void(* filter)(uint8_t *src, int stride, int qscale)
Definition: h263dsp.c:29
EIA608ToSMPTE436MContext::sample_coding_opt
unsigned sample_coding_opt
Definition: eia608_to_smpte436m.c:40
FFMAX
#define FFMAX(a, b)
Definition: macros.h:47
ff_bsf_get_packet
int ff_bsf_get_packet(AVBSFContext *ctx, AVPacket **pkt)
Called by the bitstream filters to get the next packet for filtering.
Definition: bsf.c:235
FF_SMPTE_436M_WRAPPING_TYPE_VANC_AVOPTIONS
#define FF_SMPTE_436M_WRAPPING_TYPE_VANC_AVOPTIONS(flags, unit_name)
Definition: smpte_436m_internal.h:28
av_packet_free
void av_packet_free(AVPacket **pkt)
Free the packet, if the packet is reference counted, it will be unreferenced first.
Definition: packet.c:75
AVBSFContext
The bitstream filter state.
Definition: bsf.h:68
FF_SMPTE_436M_PAYLOAD_SAMPLE_CODING_ANC_AVOPTIONS
#define FF_SMPTE_436M_PAYLOAD_SAMPLE_CODING_ANC_AVOPTIONS(flags, unit_name)
Definition: smpte_436m_internal.h:88
EIA608ToSMPTE436MContext::sample_coding
AVSmpte436mPayloadSampleCoding sample_coding
Definition: eia608_to_smpte436m.c:42
bsf.h
macros.h
fail
#define fail()
Definition: checkasm.h:199
EIA608ToSMPTE436MContext::cdp_sequence_cntr
unsigned cdp_sequence_cntr
Definition: eia608_to_smpte436m.c:38
EIA608ToSMPTE436MContext::cdp_frame_rate_byte
uint8_t cdp_frame_rate_byte
Definition: eia608_to_smpte436m.c:44
AVRational::num
int num
Numerator.
Definition: rational.h:59
EIA608ToSMPTE436MContext::wrapping_type_opt
unsigned wrapping_type_opt
Definition: eia608_to_smpte436m.c:39
avassert.h
AVSmpte291mAnc8bit::payload
uint8_t payload[AV_SMPTE_291M_ANC_PAYLOAD_CAPACITY]
Definition: smpte_436m.h:102
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:210
FF_ARRAY_ELEMS
#define FF_ARRAY_ELEMS(a)
Definition: sinewin_tablegen.c:29
av_cold
#define av_cold
Definition: attributes.h:90
codec_id.h
ff_eia608_to_smpte436m_bsf
const FFBitStreamFilter ff_eia608_to_smpte436m_bsf
Definition: eia608_to_smpte436m.c:270
intreadwrite.h
av_new_packet
int av_new_packet(AVPacket *pkt, int size)
Allocate the payload of a packet and initialize its fields with default values.
Definition: packet.c:99
av_smpte_436m_anc_encode
int av_smpte_436m_anc_encode(uint8_t *out, int size, int anc_packet_count, const AVSmpte436mCodedAnc *anc_packets)
Encode ANC packets into a single AV_CODEC_ID_SMPTE_436M_ANC AVPacket's data.
Definition: smpte_436m.c:173
ctx
AVFormatContext * ctx
Definition: movenc.c:49
AVMEDIA_TYPE_DATA
@ AVMEDIA_TYPE_DATA
Opaque data information usually continuous.
Definition: avutil.h:202
smpte_436m.h
smpte_436m_internal.h
LIBAVUTIL_VERSION_INT
#define LIBAVUTIL_VERSION_INT
Definition: version.h:85
AVClass
Describe the class of an AVClass context structure.
Definition: log.h:76
NULL
#define NULL
Definition: coverity.c:32
FFBitStreamFilter
Definition: bsf_internal.h:27
AV_WB16
#define AV_WB16(p, v)
Definition: intreadwrite.h:401
AVRational
Rational number (pair of numerator and denominator).
Definition: rational.h:58
AVSmpte291mAnc8bit::sdid_or_dbn
uint8_t sdid_or_dbn
Definition: smpte_436m.h:100
av_default_item_name
const char * av_default_item_name(void *ptr)
Return the context name.
Definition: log.c:241
options
Definition: swscale.c:43
eia608_to_smpte436m_class
static const AVClass eia608_to_smpte436m_class
Definition: eia608_to_smpte436m.c:263
AV_OPT_TYPE_UINT
@ AV_OPT_TYPE_UINT
Underlying C type is unsigned int.
Definition: opt.h:335
error.h
AVCodecID
AVCodecID
Identify the syntax and semantics of the bitstream.
Definition: codec_id.h:49
FFBitStreamFilter::p
AVBitStreamFilter p
The public AVBitStreamFilter.
Definition: bsf_internal.h:31
init
int(* init)(AVBSFContext *ctx)
Definition: dts2pts.c:368
AVPacket::size
int size
Definition: packet.h:553
ff_eia608_to_smpte436m_init
static av_cold int ff_eia608_to_smpte436m_init(AVBSFContext *ctx)
Definition: eia608_to_smpte436m.c:74
av_smpte_291m_anc_8bit_encode
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)
Encode a AVSmpte291mAnc8bit into a AVSmpte436mCodedAnc.
Definition: smpte_436m.c:375
OFFSET
#define OFFSET(x)
Definition: eia608_to_smpte436m.c:248
av_smpte_291m_anc_8bit_fill_checksum
void av_smpte_291m_anc_8bit_fill_checksum(AVSmpte291mAnc8bit *anc)
Fill in the correct checksum for a AVSmpte291mAnc8bit.
Definition: smpte_436m.c:337
AVSmpte436mPayloadSampleCoding
AVSmpte436mPayloadSampleCoding
Payload Sample Coding from Table 4 (page 10) and Table 7 (page 13) of: https://pub....
Definition: smpte_436m.h:58
ff_eia608_to_smpte436m_filter
static int ff_eia608_to_smpte436m_filter(AVBSFContext *ctx, AVPacket *out)
Definition: eia608_to_smpte436m.c:128
AVSmpte436mCodedAnc
An encoded ANC packet within a single AV_CODEC_ID_SMPTE_436M_ANC AVPacket's data.
Definition: smpte_436m.h:117
AVSmpte291mAnc8bit
An ANC packet with an 8-bit payload.
Definition: smpte_436m.h:98
av_packet_copy_props
int av_packet_copy_props(AVPacket *dst, const AVPacket *src)
Copy only "properties" fields from src to dst.
Definition: packet.c:395
AV_CODEC_ID_NONE
@ AV_CODEC_ID_NONE
Definition: codec_id.h:50
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:256
AV_CODEC_ID_SMPTE_436M_ANC
@ AV_CODEC_ID_SMPTE_436M_ANC
Definition: codec_id.h:606
AVSmpte436mWrappingType
AVSmpte436mWrappingType
Wrapping Type from Table 7 (page 13) of: https://pub.smpte.org/latest/st436/s436m-2006....
Definition: smpte_436m.h:40
EIA608ToSMPTE436MContext
Definition: eia608_to_smpte436m.c:35
ret
ret
Definition: filter_design.txt:187
AV_LOG_FATAL
#define AV_LOG_FATAL
Something went wrong and recovery is not possible.
Definition: log.h:204
AVClass::class_name
const char * class_name
The name of the class; usually it is the same name as the context structure type to which the AVClass...
Definition: log.h:81
U
#define U(x)
Definition: vpx_arith.h:37
EIA608ToSMPTE436MContext::cdp_frame_rate
AVRational cdp_frame_rate
Definition: eia608_to_smpte436m.c:43
AVRational::den
int den
Denominator.
Definition: rational.h:60
AV_SMPTE_291M_ANC_PAYLOAD_CAPACITY
#define AV_SMPTE_291M_ANC_PAYLOAD_CAPACITY
the payload capacity of AVSmpte291mAnc8bit (and of AVSmpte291mAnc10bit when that gets added)
Definition: smpte_436m.h:89
avutil.h
AVSmpte291mAnc8bit::data_count
uint8_t data_count
Definition: smpte_436m.h:101
AV_SMPTE_436M_WRAPPING_TYPE_VANC_FRAME
@ AV_SMPTE_436M_WRAPPING_TYPE_VANC_FRAME
Definition: smpte_436m.h:42
AVPacket
This structure stores compressed data.
Definition: packet.h:529
EIA608ToSMPTE436MContext::line_number
unsigned line_number
Definition: eia608_to_smpte436m.c:37
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
AVFormatContext::priv_data
void * priv_data
Format private data.
Definition: avformat.h:1292
AVSmpte291mAnc8bit::did
uint8_t did
Definition: smpte_436m.h:99