Coverage Report

Created: 2025-12-31 07:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libavcodec/bsf/eia608_to_smpte436m.c
Line
Count
Source
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
35
typedef 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
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
74
static av_cold int ff_eia608_to_smpte436m_init(AVBSFContext *ctx)
75
255
{
76
255
    EIA608ToSMPTE436MContext *priv = ctx->priv_data;
77
78
255
    priv->wrapping_type = priv->wrapping_type_opt;
79
255
    priv->sample_coding = priv->sample_coding_opt;
80
81
    // validate we can handle the selected wrapping type and sample coding
82
83
255
    AVSmpte436mCodedAnc coded_anc;
84
85
255
    int ret = av_smpte_291m_anc_8bit_encode(
86
255
        &coded_anc, priv->line_number, priv->wrapping_type, priv->sample_coding, &test_anc, ctx);
87
255
    if (ret < 0)
88
0
        return ret;
89
90
255
    ctx->par_out->codec_type = AVMEDIA_TYPE_DATA;
91
255
    ctx->par_out->codec_id   = AV_CODEC_ID_SMPTE_436M_ANC;
92
93
255
    static const struct {
94
255
        AVRational frame_rate;
95
255
        uint8_t    cdp_frame_rate;
96
255
    } known_frame_rates[] = {
97
255
        { .frame_rate = { .num = 24000, .den = 1001 }, .cdp_frame_rate = 0x1F },
98
255
        { .frame_rate = { .num = 24, .den = 1 },       .cdp_frame_rate = 0x2F },
99
255
        { .frame_rate = { .num = 25, .den = 1 },       .cdp_frame_rate = 0x3F },
100
255
        { .frame_rate = { .num = 30000, .den = 1001 }, .cdp_frame_rate = 0x4F },
101
255
        { .frame_rate = { .num = 30, .den = 1 },       .cdp_frame_rate = 0x5F },
102
255
        { .frame_rate = { .num = 50, .den = 1 },       .cdp_frame_rate = 0x6F },
103
255
        { .frame_rate = { .num = 60000, .den = 1001 }, .cdp_frame_rate = 0x7F },
104
255
        { .frame_rate = { .num = 60, .den = 1 },       .cdp_frame_rate = 0x8F },
105
255
    };
106
107
255
    priv->cdp_frame_rate_byte = 0;
108
109
1.02k
    for (int i = 0; i < FF_ARRAY_ELEMS(known_frame_rates); i++) {
110
1.02k
        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
255
            priv->cdp_frame_rate_byte = known_frame_rates[i].cdp_frame_rate;
112
255
            break;
113
255
        }
114
1.02k
    }
115
116
255
    if (priv->cdp_frame_rate_byte == 0) {
117
0
        av_log(ctx,
118
0
               AV_LOG_FATAL,
119
0
               "cdp_frame_rate not supported: %d/%d\n",
120
0
               priv->cdp_frame_rate.num,
121
0
               priv->cdp_frame_rate.den);
122
0
        return AVERROR(EINVAL);
123
0
    }
124
125
255
    return 0;
126
255
}
127
128
static int ff_eia608_to_smpte436m_filter(AVBSFContext *ctx, AVPacket *out)
129
23.5k
{
130
23.5k
    EIA608ToSMPTE436MContext *priv = ctx->priv_data;
131
23.5k
    AVPacket                 *in;
132
133
23.5k
    int ret = ff_bsf_get_packet(ctx, &in);
134
23.5k
    if (ret < 0)
135
11.8k
        return ret;
136
137
11.6k
    AVSmpte291mAnc8bit anc;
138
11.6k
    anc.did         = 0x61;
139
11.6k
    anc.sdid_or_dbn = 0x1;
140
141
11.6k
    uint8_t *p = anc.payload;
142
143
11.6k
    *p++ = 0x96; // cdp_identifier -- always 0x9669
144
11.6k
    *p++ = 0x69;
145
146
11.6k
    uint8_t *cdp_length_p = p++;
147
148
11.6k
    *p++ = priv->cdp_frame_rate_byte;
149
150
11.6k
    const uint8_t FLAG_CC_DATA_PRESENT        = 0x40;
151
11.6k
    const uint8_t FLAG_CAPTION_SERVICE_ACTIVE = 0x2;
152
11.6k
    const uint8_t FLAG_RESERVED               = 0x1; // must always be set
153
154
11.6k
    *p++ = FLAG_CC_DATA_PRESENT | FLAG_CAPTION_SERVICE_ACTIVE | FLAG_RESERVED;
155
156
11.6k
    AV_WB16(p, priv->cdp_sequence_cntr);
157
11.6k
    p += 2;
158
159
11.6k
    const uint8_t CC_DATA_SECTION_ID = 0x72;
160
161
11.6k
    *p++ = CC_DATA_SECTION_ID;
162
163
11.6k
    uint8_t *cc_count_p = p++;
164
165
11.6k
    const uint8_t CC_COUNT_MASK   = 0x1F;
166
11.6k
    const int     CDP_FOOTER_SIZE = 4;
167
168
11.6k
    int cc_count           = in->size / 3;
169
11.6k
    int space_left         = AV_SMPTE_291M_ANC_PAYLOAD_CAPACITY - (p - anc.payload);
170
11.6k
    int cc_data_space_left = space_left - CDP_FOOTER_SIZE;
171
11.6k
    int max_cc_count       = FFMAX(cc_data_space_left / 3, CC_COUNT_MASK);
172
173
11.6k
    if (cc_count > max_cc_count) {
174
1.00k
        av_log(ctx,
175
1.00k
               AV_LOG_ERROR,
176
1.00k
               "cc_count (%d) is bigger than the maximum supported (%d), truncating captions packet\n",
177
1.00k
               cc_count,
178
1.00k
               max_cc_count);
179
1.00k
        cc_count = max_cc_count;
180
1.00k
    }
181
182
11.6k
    *cc_count_p = cc_count | ~CC_COUNT_MASK; // other bits are reserved and set to ones
183
184
135k
    for (size_t i = 0; i < cc_count; i++) {
185
124k
        size_t start = i * 3;
186
124k
        *p++         = in->data[start] | 0xF8; // fill reserved bits with ones
187
124k
        *p++         = in->data[start + 1];
188
124k
        *p++         = in->data[start + 2];
189
124k
    }
190
191
11.6k
    const uint8_t CDP_FOOTER_ID = 0x74;
192
193
11.6k
    *p++ = CDP_FOOTER_ID;
194
195
11.6k
    AV_WB16(p, priv->cdp_sequence_cntr);
196
11.6k
    p += 2;
197
198
11.6k
    uint8_t *packet_checksum_p = p;
199
11.6k
    *p++                       = 0;
200
201
11.6k
    anc.data_count = p - anc.payload;
202
11.6k
    *cdp_length_p  = anc.data_count;
203
204
11.6k
    int sum = 0;
205
535k
    for (int i = 0; i < anc.data_count; i++) {
206
523k
        sum += anc.payload[i];
207
523k
    }
208
    // set to an 8-bit value such that the sum of the bytes of the whole CDP mod 2^8 is 0
209
11.6k
    *packet_checksum_p = -sum;
210
211
11.6k
    priv->cdp_sequence_cntr++;
212
    // cdp_sequence_cntr wraps around at 16-bits
213
11.6k
    priv->cdp_sequence_cntr &= 0xFFFFU;
214
215
11.6k
    av_smpte_291m_anc_8bit_fill_checksum(&anc);
216
217
11.6k
    AVSmpte436mCodedAnc coded_anc;
218
11.6k
    ret = av_smpte_291m_anc_8bit_encode(
219
11.6k
        &coded_anc, priv->line_number, (AVSmpte436mWrappingType)priv->wrapping_type, priv->sample_coding, &anc, ctx);
220
11.6k
    if (ret < 0)
221
0
        goto fail;
222
223
11.6k
    ret = av_smpte_436m_anc_encode(NULL, 0, 1, &coded_anc);
224
11.6k
    if (ret < 0)
225
0
        goto fail;
226
227
11.6k
    ret = av_new_packet(out, ret);
228
11.6k
    if (ret < 0)
229
0
        goto fail;
230
231
11.6k
    ret = av_packet_copy_props(out, in);
232
11.6k
    if (ret < 0)
233
0
        goto fail;
234
235
11.6k
    ret = av_smpte_436m_anc_encode(out->data, out->size, 1, &coded_anc);
236
11.6k
    if (ret < 0)
237
0
        goto fail;
238
239
11.6k
    ret = 0;
240
241
11.6k
fail:
242
11.6k
    if (ret < 0)
243
0
        av_packet_unref(out);
244
11.6k
    av_packet_free(&in);
245
11.6k
    return ret;
246
11.6k
}
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" },
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
263
static 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
270
const 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
};