Coverage Report

Created: 2026-04-01 07:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libavcodec/dvbsubenc.c
Line
Count
Source
1
/*
2
 * DVB subtitle encoding
3
 * Copyright (c) 2005 Fabrice Bellard
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
#include "avcodec.h"
22
#include "bytestream.h"
23
#include "codec_internal.h"
24
#include "libavutil/colorspace.h"
25
#include "libavutil/opt.h"
26
27
typedef struct DVBSubtitleContext {
28
    AVClass * class;
29
    int object_version;
30
    int min_bpp;
31
} DVBSubtitleContext;
32
33
0
#define PUTBITS2(val)\
34
0
{\
35
0
    bitbuf |= (val) << bitcnt;\
36
0
    bitcnt -= 2;\
37
0
    if (bitcnt < 0) {\
38
0
        bitcnt = 6;\
39
0
        *q++ = bitbuf;\
40
0
        bitbuf = 0;\
41
0
    }\
42
0
}
43
44
static int dvb_encode_rle2(uint8_t **pq, int buf_size,
45
                           const uint8_t *bitmap, int linesize,
46
                           int w, int h)
47
0
{
48
0
    uint8_t *q, *line_begin;
49
0
    unsigned int bitbuf;
50
0
    int bitcnt;
51
0
    int x, y, len, x1, v, color;
52
53
0
    q = *pq;
54
55
0
    for(y = 0; y < h; y++) {
56
        // Worst case line is 3 bits per value + 4 bytes overhead
57
0
        if (buf_size * 8 < w * 3 + 32)
58
0
            return AVERROR_BUFFER_TOO_SMALL;
59
0
        line_begin = q;
60
0
        *q++ = 0x10;
61
0
        bitbuf = 0;
62
0
        bitcnt = 6;
63
64
0
        x = 0;
65
0
        while (x < w) {
66
0
            x1 = x;
67
0
            color = bitmap[x1++];
68
0
            while (x1 < w && bitmap[x1] == color)
69
0
                x1++;
70
0
            len = x1 - x;
71
0
            if (color == 0 && len == 2) {
72
0
                PUTBITS2(0);
73
0
                PUTBITS2(0);
74
0
                PUTBITS2(1);
75
0
            } else if (len >= 3 && len <= 10) {
76
0
                v = len - 3;
77
0
                PUTBITS2(0);
78
0
                PUTBITS2((v >> 2) | 2);
79
0
                PUTBITS2(v & 3);
80
0
                PUTBITS2(color);
81
0
            } else if (len >= 12 && len <= 27) {
82
0
                v = len - 12;
83
0
                PUTBITS2(0);
84
0
                PUTBITS2(0);
85
0
                PUTBITS2(2);
86
0
                PUTBITS2(v >> 2);
87
0
                PUTBITS2(v & 3);
88
0
                PUTBITS2(color);
89
0
            } else if (len >= 29) {
90
                /* length = 29 ... 284 */
91
0
                if (len > 284)
92
0
                    len = 284;
93
0
                v = len - 29;
94
0
                PUTBITS2(0);
95
0
                PUTBITS2(0);
96
0
                PUTBITS2(3);
97
0
                PUTBITS2((v >> 6));
98
0
                PUTBITS2((v >> 4) & 3);
99
0
                PUTBITS2((v >> 2) & 3);
100
0
                PUTBITS2(v & 3);
101
0
                PUTBITS2(color);
102
0
            } else {
103
0
                PUTBITS2(color);
104
0
                if (color == 0) {
105
0
                    PUTBITS2(1);
106
0
                }
107
0
                len = 1;
108
0
            }
109
0
            x += len;
110
0
        }
111
        /* end of line */
112
0
        PUTBITS2(0);
113
0
        PUTBITS2(0);
114
0
        PUTBITS2(0);
115
0
        if (bitcnt != 6) {
116
0
            *q++ = bitbuf;
117
0
        }
118
0
        *q++ = 0xf0;
119
0
        bitmap += linesize;
120
0
        buf_size -= q - line_begin;
121
0
    }
122
0
    len = q - *pq;
123
0
    *pq = q;
124
0
    return len;
125
0
}
126
127
0
#define PUTBITS4(val)\
128
0
{\
129
0
    bitbuf |= (val) << bitcnt;\
130
0
    bitcnt -= 4;\
131
0
    if (bitcnt < 0) {\
132
0
        bitcnt = 4;\
133
0
        *q++ = bitbuf;\
134
0
        bitbuf = 0;\
135
0
    }\
136
0
}
137
138
/* some DVB decoders only implement 4 bits/pixel */
139
static int dvb_encode_rle4(uint8_t **pq, int buf_size,
140
                           const uint8_t *bitmap, int linesize,
141
                           int w, int h)
142
0
{
143
0
    uint8_t *q, *line_begin;
144
0
    unsigned int bitbuf;
145
0
    int bitcnt;
146
0
    int x, y, len, x1, v, color;
147
148
0
    q = *pq;
149
150
0
    for(y = 0; y < h; y++) {
151
        // Worst case line is 6 bits per value, + 4 bytes overhead
152
0
        if (buf_size * 8 < w * 6 + 32)
153
0
            return AVERROR_BUFFER_TOO_SMALL;
154
0
        line_begin = q;
155
0
        *q++ = 0x11;
156
0
        bitbuf = 0;
157
0
        bitcnt = 4;
158
159
0
        x = 0;
160
0
        while (x < w) {
161
0
            x1 = x;
162
0
            color = bitmap[x1++];
163
0
            while (x1 < w && bitmap[x1] == color)
164
0
                x1++;
165
0
            len = x1 - x;
166
0
            if (color == 0 && len == 2) {
167
0
                PUTBITS4(0);
168
0
                PUTBITS4(0xd);
169
0
            } else if (color == 0 && (len >= 3 && len <= 9)) {
170
0
                PUTBITS4(0);
171
0
                PUTBITS4(len - 2);
172
0
            } else if (len >= 4 && len <= 7) {
173
0
                PUTBITS4(0);
174
0
                PUTBITS4(8 + len - 4);
175
0
                PUTBITS4(color);
176
0
            } else if (len >= 9 && len <= 24) {
177
0
                PUTBITS4(0);
178
0
                PUTBITS4(0xe);
179
0
                PUTBITS4(len - 9);
180
0
                PUTBITS4(color);
181
0
            } else if (len >= 25) {
182
0
                if (len > 280)
183
0
                    len = 280;
184
0
                v = len - 25;
185
0
                PUTBITS4(0);
186
0
                PUTBITS4(0xf);
187
0
                PUTBITS4(v >> 4);
188
0
                PUTBITS4(v & 0xf);
189
0
                PUTBITS4(color);
190
0
            } else {
191
0
                PUTBITS4(color);
192
0
                if (color == 0) {
193
0
                    PUTBITS4(0xc);
194
0
                }
195
0
                len = 1;
196
0
            }
197
0
            x += len;
198
0
        }
199
        /* end of line */
200
0
        PUTBITS4(0);
201
0
        PUTBITS4(0);
202
0
        if (bitcnt != 4) {
203
0
            *q++ = bitbuf;
204
0
        }
205
0
        *q++ = 0xf0;
206
0
        bitmap += linesize;
207
0
        buf_size -= q - line_begin;
208
0
    }
209
0
    len = q - *pq;
210
0
    *pq = q;
211
0
    return len;
212
0
}
213
214
static int dvb_encode_rle8(uint8_t **pq, int buf_size,
215
                           const uint8_t *bitmap, int linesize,
216
                           int w, int h)
217
0
{
218
0
    uint8_t *q, *line_begin;
219
0
    int x, y, len, x1, color;
220
221
0
    q = *pq;
222
223
0
    for (y = 0; y < h; y++) {
224
        // Worst case line is 12 bits per value, + 3 bytes overhead
225
0
        if (buf_size * 8 < w * 12 + 24)
226
0
            return AVERROR_BUFFER_TOO_SMALL;
227
0
        line_begin = q;
228
0
        *q++ = 0x12;
229
230
0
        x = 0;
231
0
        while (x < w) {
232
0
            x1 = x;
233
0
            color = bitmap[x1++];
234
0
            while (x1 < w && bitmap[x1] == color)
235
0
                x1++;
236
0
            len = x1 - x;
237
0
            if (len == 1 && color) {
238
                // 00000001 to 11111111           1 pixel in colour x
239
0
                *q++ = color;
240
0
            } else {
241
0
                if (color == 0x00) {
242
                    // 00000000 0LLLLLLL          L pixels (1-127) in colour 0 (L > 0)
243
0
                    len = FFMIN(len, 127);
244
0
                    *q++ = 0x00;
245
0
                    *q++ = len;
246
0
                } else if (len > 2) {
247
                    // 00000000 1LLLLLLL CCCCCCCC L pixels (3-127) in colour C (L > 2)
248
0
                    len = FFMIN(len, 127);
249
0
                    *q++ = 0x00;
250
0
                    *q++ = 0x80+len;
251
0
                    *q++ = color;
252
0
                }
253
0
                else if (len == 2) {
254
0
                    *q++ = color;
255
0
                    *q++ = color;
256
0
                } else {
257
0
                    *q++ = color;
258
0
                    len = 1;
259
0
                }
260
0
            }
261
0
            x += len;
262
0
        }
263
        /* end of line */
264
        // 00000000 00000000 end of 8-bit/pixel_code_string
265
0
        *q++ = 0x00;
266
0
        *q++ = 0x00;
267
0
        *q++ = 0xf0;
268
0
        bitmap += linesize;
269
0
        buf_size -= q - line_begin;
270
0
    }
271
0
    len = q - *pq;
272
0
    *pq = q;
273
0
    return len;
274
0
}
275
276
static int dvbsub_encode(AVCodecContext *avctx, uint8_t *outbuf, int buf_size,
277
                         const AVSubtitle *h)
278
0
{
279
0
    DVBSubtitleContext *s = avctx->priv_data;
280
0
    uint8_t *q, *pseg_len;
281
0
    int page_id, region_id, clut_id, object_id, i, bpp_index, page_state, min_colors;
282
283
284
0
    q = outbuf;
285
286
0
    page_id = 1;
287
288
0
    switch(s->min_bpp) {
289
0
    case 2:
290
0
    case 4:
291
0
    case 8:
292
0
      min_colors = 1 << s->min_bpp;
293
0
      break;
294
0
    default:
295
0
      av_log(avctx, AV_LOG_ERROR, "Invalid min_bpp value %d.\n", s->min_bpp);
296
0
      return AVERROR(EINVAL);
297
0
    }
298
299
0
    if (h->num_rects && !h->rects)
300
0
        return AVERROR(EINVAL);
301
302
0
    if (h->num_rects >= 256)
303
0
        return AVERROR(EINVAL);
304
305
0
    if (avctx->width > 0 && avctx->height > 0) {
306
0
        if (buf_size < 11)
307
0
            return AVERROR_BUFFER_TOO_SMALL;
308
        /* display definition segment */
309
0
        *q++ = 0x0f; /* sync_byte */
310
0
        *q++ = 0x14; /* segment_type */
311
0
        bytestream_put_be16(&q, page_id);
312
0
        pseg_len = q;
313
0
        q += 2; /* segment length */
314
0
        *q++ = 0x00; /* dds version number & display window flag */
315
0
        bytestream_put_be16(&q, avctx->width - 1); /* display width */
316
0
        bytestream_put_be16(&q, avctx->height - 1); /* display height */
317
0
        bytestream_put_be16(&pseg_len, q - pseg_len - 2);
318
0
        buf_size -= 11;
319
0
    }
320
321
    /* page composition segment */
322
323
0
    if (buf_size < 8 + h->num_rects * 6)
324
0
        return AVERROR_BUFFER_TOO_SMALL;
325
0
    *q++ = 0x0f; /* sync_byte */
326
0
    *q++ = 0x10; /* segment_type */
327
0
    bytestream_put_be16(&q, page_id);
328
0
    pseg_len = q;
329
0
    q += 2; /* segment length */
330
0
    *q++ = 30; /* page_timeout (seconds) */
331
0
    page_state = 2; /* mode change */
332
    /* page_version = 0 + page_state */
333
0
    *q++ = (s->object_version << 4) | (page_state << 2) | 3;
334
335
0
    for (region_id = 0; region_id < h->num_rects; region_id++) {
336
0
        *q++ = region_id;
337
0
        *q++ = 0xff; /* reserved */
338
0
        bytestream_put_be16(&q, h->rects[region_id]->x); /* left pos */
339
0
        bytestream_put_be16(&q, h->rects[region_id]->y); /* top pos */
340
0
    }
341
342
0
    bytestream_put_be16(&pseg_len, q - pseg_len - 2);
343
0
    buf_size -= 8 + h->num_rects * 6;
344
345
0
    if (h->num_rects) {
346
0
        for (clut_id = 0; clut_id < h->num_rects; clut_id++) {
347
            /* CLUT segment */
348
0
            int nb_colors = FFMAX(min_colors, h->rects[clut_id]->nb_colors);
349
350
0
            if (nb_colors <= 4U) {
351
                /* 2 bpp, some decoders do not support it correctly */
352
0
                bpp_index = 0;
353
0
            } else if (nb_colors <= 16U) {
354
                /* 4 bpp, standard encoding */
355
0
                bpp_index = 1;
356
0
            } else if (nb_colors <= 256U) {
357
                /* 8 bpp, standard encoding */
358
0
                bpp_index = 2;
359
0
            } else {
360
0
                return AVERROR(EINVAL);
361
0
            }
362
363
0
            if (buf_size < 6 + h->rects[clut_id]->nb_colors * 6)
364
0
                return AVERROR_BUFFER_TOO_SMALL;
365
366
            /* CLUT segment */
367
0
            *q++ = 0x0f; /* sync byte */
368
0
            *q++ = 0x12; /* CLUT definition segment */
369
0
            bytestream_put_be16(&q, page_id);
370
0
            pseg_len = q;
371
0
            q += 2; /* segment length */
372
0
            *q++ = clut_id;
373
0
            *q++ = (0 << 4) | 0xf; /* version = 0 */
374
375
0
            for(i = 0; i < h->rects[clut_id]->nb_colors; i++) {
376
0
                *q++ = i; /* clut_entry_id */
377
0
                *q++ = (1 << (7 - bpp_index)) | (0xf << 1) | 1; /* 2 bits/pixel full range */
378
0
                {
379
0
                    int a, r, g, b;
380
0
                    uint32_t x= ((uint32_t*)h->rects[clut_id]->data[1])[i];
381
0
                    a = (x >> 24) & 0xff;
382
0
                    r = (x >> 16) & 0xff;
383
0
                    g = (x >>  8) & 0xff;
384
0
                    b = (x >>  0) & 0xff;
385
386
0
                    *q++ = RGB_TO_Y_CCIR(r, g, b);
387
0
                    *q++ = RGB_TO_V_CCIR(r, g, b, 0);
388
0
                    *q++ = RGB_TO_U_CCIR(r, g, b, 0);
389
0
                    *q++ = 255 - a;
390
0
                }
391
0
            }
392
393
0
            bytestream_put_be16(&pseg_len, q - pseg_len - 2);
394
0
            buf_size -= 6 + h->rects[clut_id]->nb_colors * 6;
395
0
        }
396
397
0
        if (buf_size < h->num_rects * 22)
398
0
            return AVERROR_BUFFER_TOO_SMALL;
399
0
        for (region_id = 0; region_id < h->num_rects; region_id++) {
400
401
            /* region composition segment */
402
0
            int nb_colors = FFMAX(min_colors, h->rects[region_id]->nb_colors);
403
404
0
            if (nb_colors <= 4) {
405
                /* 2 bpp, some decoders do not support it correctly */
406
0
                bpp_index = 0;
407
0
            } else if (nb_colors <= 16) {
408
                /* 4 bpp, standard encoding */
409
0
                bpp_index = 1;
410
0
            } else if (nb_colors <= 256) {
411
                /* 8 bpp, standard encoding */
412
0
                bpp_index = 2;
413
0
            } else {
414
0
                return AVERROR(EINVAL);
415
0
            }
416
417
0
            *q++ = 0x0f; /* sync_byte */
418
0
            *q++ = 0x11; /* segment_type */
419
0
            bytestream_put_be16(&q, page_id);
420
0
            pseg_len = q;
421
0
            q += 2; /* segment length */
422
0
            *q++ = region_id;
423
0
            *q++ = (s->object_version << 4) | (0 << 3) | 0x07; /* version , no fill */
424
0
            bytestream_put_be16(&q, h->rects[region_id]->w); /* region width */
425
0
            bytestream_put_be16(&q, h->rects[region_id]->h); /* region height */
426
0
            *q++ = ((1 + bpp_index) << 5) | ((1 + bpp_index) << 2) | 0x03;
427
0
            *q++ = region_id; /* clut_id == region_id */
428
0
            *q++ = 0; /* 8 bit fill colors */
429
0
            *q++ = 0x03; /* 4 bit and 2 bit fill colors */
430
431
0
            bytestream_put_be16(&q, region_id); /* object_id == region_id */
432
0
            *q++ = (0 << 6) | (0 << 4);
433
0
            *q++ = 0;
434
0
            *q++ = 0xf0;
435
0
            *q++ = 0;
436
437
0
            bytestream_put_be16(&pseg_len, q - pseg_len - 2);
438
0
        }
439
0
        buf_size -= h->num_rects * 22;
440
441
0
        for (object_id = 0; object_id < h->num_rects; object_id++) {
442
0
            int (*dvb_encode_rle)(uint8_t **pq, int buf_size,
443
0
                                  const uint8_t *bitmap, int linesize,
444
0
                                  int w, int h);
445
446
0
            int nb_colors = FFMAX(min_colors, h->rects[object_id]->nb_colors);
447
448
0
            if (buf_size < 13)
449
0
                return AVERROR_BUFFER_TOO_SMALL;
450
451
            /* bpp_index maths */
452
0
            if (nb_colors <= 4) {
453
                /* 2 bpp, some decoders do not support it correctly */
454
0
                dvb_encode_rle = dvb_encode_rle2;
455
0
            } else if (nb_colors <= 16) {
456
                /* 4 bpp, standard encoding */
457
0
                dvb_encode_rle = dvb_encode_rle4;
458
0
            } else if (nb_colors <= 256) {
459
                /* 8 bpp, standard encoding */
460
0
                dvb_encode_rle = dvb_encode_rle8;
461
0
            } else {
462
0
                return AVERROR(EINVAL);
463
0
            }
464
465
            /* Object Data segment */
466
0
            *q++ = 0x0f; /* sync byte */
467
0
            *q++ = 0x13;
468
0
            bytestream_put_be16(&q, page_id);
469
0
            pseg_len = q;
470
0
            q += 2; /* segment length */
471
472
0
            bytestream_put_be16(&q, object_id);
473
0
            *q++ = (s->object_version << 4) | (0 << 2) | (0 << 1) | 1; /* version = 0,
474
                                                                       object_coding_method,
475
                                                                       non_modifying_color_flag */
476
0
            {
477
0
                uint8_t *ptop_field_len, *pbottom_field_len, *top_ptr, *bottom_ptr;
478
0
                int ret;
479
480
0
                ptop_field_len = q;
481
0
                q += 2;
482
0
                pbottom_field_len = q;
483
0
                q += 2;
484
0
                buf_size -= 13;
485
486
0
                top_ptr = q;
487
0
                ret = dvb_encode_rle(&q, buf_size,
488
0
                                     h->rects[object_id]->data[0],
489
0
                                     h->rects[object_id]->w * 2,
490
0
                                     h->rects[object_id]->w,
491
0
                                     h->rects[object_id]->h >> 1);
492
0
                if (ret < 0)
493
0
                    return ret;
494
0
                buf_size -= ret;
495
0
                bottom_ptr = q;
496
0
                ret = dvb_encode_rle(&q, buf_size,
497
0
                                     h->rects[object_id]->data[0] + h->rects[object_id]->w,
498
0
                                     h->rects[object_id]->w * 2,
499
0
                                     h->rects[object_id]->w,
500
0
                                     h->rects[object_id]->h >> 1);
501
0
                if (ret < 0)
502
0
                    return ret;
503
0
                buf_size -= ret;
504
505
0
                bytestream_put_be16(&ptop_field_len, bottom_ptr - top_ptr);
506
0
                bytestream_put_be16(&pbottom_field_len, q - bottom_ptr);
507
0
            }
508
509
0
            bytestream_put_be16(&pseg_len, q - pseg_len - 2);
510
0
        }
511
0
    }
512
513
    /* end of display set segment */
514
515
0
    if (buf_size < 6)
516
0
        return AVERROR_BUFFER_TOO_SMALL;
517
0
    *q++ = 0x0f; /* sync_byte */
518
0
    *q++ = 0x80; /* segment_type */
519
0
    bytestream_put_be16(&q, page_id);
520
0
    pseg_len = q;
521
0
    q += 2; /* segment length */
522
523
0
    bytestream_put_be16(&pseg_len, q - pseg_len - 2);
524
0
    buf_size -= 6;
525
526
0
    s->object_version = (s->object_version + 1) & 0xf;
527
0
    return q - outbuf;
528
0
}
529
530
#define OFFSET(x) offsetof(DVBSubtitleContext, x)
531
#define SE AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_ENCODING_PARAM
532
static const AVOption options[] = {
533
    {"min_bpp", "minimum bits-per-pixel for subtitle colors (2, 4 or 8)", OFFSET(min_bpp), AV_OPT_TYPE_INT, {.i64 = 4}, 2, 8, SE},
534
    { NULL },
535
};
536
537
static const AVClass dvbsubenc_class = {
538
    .class_name = "DVBSUB subtitle encoder",
539
    .item_name  = av_default_item_name,
540
    .option     = options,
541
    .version    = LIBAVUTIL_VERSION_INT,
542
};
543
544
const FFCodec ff_dvbsub_encoder = {
545
    .p.name         = "dvbsub",
546
    CODEC_LONG_NAME("DVB subtitles"),
547
    .p.type         = AVMEDIA_TYPE_SUBTITLE,
548
    .p.id           = AV_CODEC_ID_DVB_SUBTITLE,
549
    .priv_data_size = sizeof(DVBSubtitleContext),
550
    FF_CODEC_ENCODE_SUB_CB(dvbsub_encode),
551
    .p.priv_class   = &dvbsubenc_class,
552
};