Coverage Report

Created: 2026-08-14 07:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/modules/codec/avcodec/subtitle.c
Line
Count
Source
1
/*****************************************************************************
2
 * subtitle.c: subtitle decoder using libavcodec library
3
 *****************************************************************************
4
 * Copyright (C) 2009 Laurent Aimar
5
 *
6
 * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
7
 *
8
 * This program is free software; you can redistribute it and/or modify it
9
 * under the terms of the GNU Lesser General Public License as published by
10
 * the Free Software Foundation; either version 2.1 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
 * GNU Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public License
19
 * along with this program; if not, write to the Free Software Foundation,
20
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21
 *****************************************************************************/
22
23
/*****************************************************************************
24
 * Preamble
25
 *****************************************************************************/
26
#ifdef HAVE_CONFIG_H
27
# include "config.h"
28
#endif
29
#include <assert.h>
30
31
#include <vlc_common.h>
32
#include <vlc_codec.h>
33
#include <vlc_avcodec.h>
34
35
#include <libavcodec/avcodec.h>
36
#include <libavutil/mem.h>
37
#include <libavutil/opt.h>
38
39
#include "avcodec.h"
40
41
typedef struct
42
{
43
    AVCodecContext *p_context;
44
    const AVCodec  *p_codec;
45
    bool b_need_ephemer; /* Does the format need the ephemer flag (no end time set) */
46
} decoder_sys_t;
47
48
static subpicture_t *ConvertSubtitle(decoder_t *, AVSubtitle *, vlc_tick_t pts,
49
                                     AVCodecContext *avctx);
50
static int  DecodeSubtitle(decoder_t *, block_t *);
51
static void Flush(decoder_t *);
52
53
/**
54
 * Initialize subtitle decoder
55
 */
56
int InitSubtitleDec(vlc_object_t *obj)
57
17.6k
{
58
17.6k
    decoder_t *dec = (decoder_t *)obj;
59
17.6k
    const AVCodec *codec;
60
17.6k
    AVCodecContext *context = ffmpeg_AllocContext(dec, &codec, false);
61
17.6k
    if (context == NULL)
62
14.2k
        return VLC_EGENERIC;
63
64
3.45k
    decoder_sys_t *sys;
65
66
    /* */
67
3.45k
    switch (codec->id) {
68
3
    case AV_CODEC_ID_HDMV_PGS_SUBTITLE:
69
        /* a track marked forced shows only its forced captions */
70
3
        if (dec->fmt_in->subs.b_forced) {
71
0
            av_opt_set_int(context->priv_data, "forced_subs_only", 1, 0);
72
0
        }
73
3
        break;
74
0
    case AV_CODEC_ID_XSUB:
75
0
    case AV_CODEC_ID_DVB_SUBTITLE:
76
0
        break;
77
3.45k
    default:
78
3.45k
        msg_Warn(dec, "refusing to decode non validated subtitle codec");
79
3.45k
        avcodec_free_context(&context);
80
3.45k
        return VLC_EGENERIC;
81
3.45k
    }
82
83
    /* */
84
3
    dec->p_sys = sys = malloc(sizeof(*sys));
85
3
    if (unlikely(sys == NULL))
86
0
    {
87
0
        avcodec_free_context(&context);
88
0
        return VLC_ENOMEM;
89
0
    }
90
91
3
    sys->p_context = context;
92
3
    sys->p_codec = codec;
93
3
    sys->b_need_ephemer = codec->id == AV_CODEC_ID_HDMV_PGS_SUBTITLE;
94
95
    /* */
96
3
    context->extradata_size = 0;
97
3
    context->extradata = NULL;
98
99
3
    if( codec->id == AV_CODEC_ID_DVB_SUBTITLE )
100
0
    {
101
0
        if( dec->fmt_in->i_extra > 3 )
102
0
        {
103
0
            context->extradata = malloc( dec->fmt_in->i_extra );
104
0
            if( context->extradata )
105
0
            {
106
0
                context->extradata_size = dec->fmt_in->i_extra;
107
0
                memcpy( context->extradata, dec->fmt_in->p_extra, dec->fmt_in->i_extra );
108
0
            }
109
0
        }
110
0
        else
111
0
        {
112
0
            context->extradata = malloc( 4 );
113
0
            if( context->extradata )
114
0
            {
115
0
                context->extradata_size = 4;
116
0
                SetWBE( &context->extradata[0], dec->fmt_in->subs.dvb.i_id & 0xFFFF );
117
0
                SetWBE( &context->extradata[2], dec->fmt_in->subs.dvb.i_id >> 16 );
118
0
            }
119
0
        }
120
0
    }
121
122
3
    context->pkt_timebase=AV_TIME_BASE_Q;
123
124
    /* */
125
3
    int ret;
126
3
    char *psz_opts = var_InheritString(dec, "avcodec-options");
127
3
    AVDictionary *options = NULL;
128
3
    if (psz_opts) {
129
0
        vlc_av_get_options(psz_opts, &options);
130
0
        free(psz_opts);
131
0
    }
132
133
3
    vlc_avcodec_lock();
134
3
    ret = avcodec_open2(context, codec, options ? &options : NULL);
135
3
    vlc_avcodec_unlock();
136
137
3
    AVDictionaryEntry *t = NULL;
138
3
    while ((t = av_dict_get(options, "", t, AV_DICT_IGNORE_SUFFIX))) {
139
0
        msg_Err(dec, "Unknown option \"%s\"", t->key);
140
0
    }
141
3
    av_dict_free(&options);
142
143
3
    if (ret < 0) {
144
0
        msg_Err(dec, "cannot open codec (%s)", codec->name);
145
0
        free(sys);
146
0
        avcodec_free_context(&context);
147
0
        return VLC_EGENERIC;
148
0
    }
149
150
    /* */
151
3
    msg_Dbg(dec, "libavcodec codec (%s) started", codec->name);
152
3
    dec->pf_decode = DecodeSubtitle;
153
3
    dec->pf_flush  = Flush;
154
155
3
    return VLC_SUCCESS;
156
3
}
157
158
void EndSubtitleDec(vlc_object_t *obj)
159
3
{
160
3
    decoder_t *dec = (decoder_t *)obj;
161
3
    decoder_sys_t *sys = dec->p_sys;
162
3
    AVCodecContext *ctx = sys->p_context;
163
164
3
    avcodec_free_context(&ctx);
165
3
    free(sys);
166
3
}
167
168
/**
169
 * Flush
170
 */
171
static void Flush(decoder_t *dec)
172
0
{
173
0
    decoder_sys_t *sys = dec->p_sys;
174
175
0
    avcodec_flush_buffers(sys->p_context);
176
0
}
177
178
/**
179
 * Decode one subtitle
180
 */
181
static subpicture_t *DecodeBlock(decoder_t *dec, block_t **block_ptr)
182
3
{
183
3
    decoder_sys_t *sys = dec->p_sys;
184
185
3
    if (!block_ptr || !*block_ptr)
186
3
        return NULL;
187
188
0
    block_t *block = *block_ptr;
189
190
0
    if (block->i_flags & (BLOCK_FLAG_DISCONTINUITY | BLOCK_FLAG_CORRUPTED)) {
191
0
        if (block->i_flags & BLOCK_FLAG_CORRUPTED) {
192
0
            Flush(dec);
193
0
            block_Release(block);
194
0
            return NULL;
195
0
        }
196
0
    }
197
198
0
    if (block->i_buffer <= 0) {
199
0
        block_Release(block);
200
0
        return NULL;
201
0
    }
202
203
0
    *block_ptr =
204
0
    block      = block_Realloc(block,
205
0
                               0,
206
0
                               block->i_buffer + AV_INPUT_BUFFER_PADDING_SIZE);
207
0
    if (!block)
208
0
        return NULL;
209
0
    block->i_buffer -= AV_INPUT_BUFFER_PADDING_SIZE;
210
0
    memset(&block->p_buffer[block->i_buffer], 0, AV_INPUT_BUFFER_PADDING_SIZE);
211
212
0
    if( sys->p_codec->id == AV_CODEC_ID_DVB_SUBTITLE && block->i_buffer > 3 )
213
0
    {
214
0
        block->p_buffer += 2; /* drop data identifier / stream id */
215
0
        block->i_buffer -= 3; /* drop 0x3F/FF */
216
0
    }
217
218
    /* */
219
0
    AVSubtitle subtitle;
220
0
    memset(&subtitle, 0, sizeof(subtitle));
221
222
0
    AVPacket *pkt = av_packet_alloc();
223
0
    if(!pkt)
224
0
    {
225
0
        block_Release(block);
226
0
        return NULL;
227
0
    }
228
0
    pkt->data = block->p_buffer;
229
0
    pkt->size = block->i_buffer;
230
0
    pkt->pts  = TO_AV_TS(block->i_pts);
231
232
0
    int has_subtitle = 0;
233
0
    int used = avcodec_decode_subtitle2(sys->p_context,
234
0
                                        &subtitle, &has_subtitle, pkt);
235
236
0
    av_packet_free(&pkt);
237
0
    if (used < 0) {
238
0
        msg_Warn(dec, "cannot decode one subtitle (%zu bytes)",
239
0
                 block->i_buffer);
240
241
0
        block_Release(block);
242
0
        return NULL;
243
0
    } else if ((size_t)used > block->i_buffer) {
244
0
        used = block->i_buffer;
245
0
    }
246
247
0
    block->i_buffer -= used;
248
0
    block->p_buffer += used;
249
250
    /* */
251
0
    subpicture_t *spu = NULL;
252
0
    if (has_subtitle)
253
0
        spu = ConvertSubtitle(dec, &subtitle,
254
0
                              FROM_AV_TS(subtitle.pts),
255
0
                              sys->p_context);
256
257
    /* */
258
0
    if (!spu)
259
0
        block_Release(block);
260
0
    return spu;
261
0
}
262
263
static int DecodeSubtitle(decoder_t *dec, block_t *block)
264
3
{
265
3
    block_t **block_ptr = block ? &block : NULL;
266
3
    subpicture_t *spu;
267
3
    while ((spu = DecodeBlock(dec, block_ptr)) != NULL)
268
0
        decoder_QueueSub(dec, spu);
269
3
    return VLCDEC_SUCCESS;
270
3
}
271
272
/**
273
 * Convert a RGBA libavcodec region to our format.
274
 */
275
static subpicture_region_t *ConvertRegionRGBA(AVSubtitleRect *ffregion)
276
0
{
277
0
    if (ffregion->w <= 0 || ffregion->h <= 0)
278
0
        return NULL;
279
280
0
    video_format_t fmt;
281
0
    memset(&fmt, 0, sizeof(fmt));
282
0
    fmt.i_chroma         = VLC_CODEC_RGBA;
283
0
    fmt.i_width          =
284
0
    fmt.i_visible_width  = ffregion->w;
285
0
    fmt.i_height         =
286
0
    fmt.i_visible_height = ffregion->h;
287
0
    fmt.i_x_offset       = 0;
288
0
    fmt.i_y_offset       = 0;
289
290
0
    subpicture_region_t *region = subpicture_region_New(&fmt);
291
0
    if (!region)
292
0
        return NULL;
293
294
0
    region->b_absolute = true; region->b_in_window = false; /* We have offset and size for subtitle */
295
0
    region->i_x = ffregion->x;
296
0
    region->i_y = ffregion->y;
297
0
    region->i_align = SUBPICTURE_ALIGN_TOP | SUBPICTURE_ALIGN_LEFT;
298
299
0
    const plane_t *p = &region->p_picture->p[0];
300
0
    for (int y = 0; y < ffregion->h; y++) {
301
0
        for (int x = 0; x < ffregion->w; x++) {
302
            /* I don't think don't have paletized RGB_A_ */
303
0
            const uint8_t index = ffregion->data[0][y * ffregion->w+x];
304
0
            assert(index < ffregion->nb_colors);
305
306
0
            uint32_t color;
307
0
            memcpy(&color, &ffregion->data[1][4*index], 4);
308
309
0
            uint8_t *p_rgba = &p->p_pixels[y * p->i_pitch + x * p->i_pixel_pitch];
310
0
            p_rgba[0] = (color >> 16) & 0xff;
311
0
            p_rgba[1] = (color >>  8) & 0xff;
312
0
            p_rgba[2] = (color >>  0) & 0xff;
313
0
            p_rgba[3] = (color >> 24) & 0xff;
314
0
        }
315
0
    }
316
317
0
    return region;
318
0
}
319
320
/**
321
 * Convert a libavcodec subtitle to our format.
322
 */
323
static subpicture_t *ConvertSubtitle(decoder_t *dec, AVSubtitle *ffsub, vlc_tick_t pts,
324
                                     AVCodecContext *avctx)
325
0
{
326
0
    subpicture_t *spu = decoder_NewSubpicture(dec, NULL);
327
0
    if (!spu)
328
0
        return NULL;
329
330
0
    decoder_sys_t *p_sys = dec->p_sys;
331
332
    //msg_Err(dec, "%lld %d %d",
333
    //        pts, ffsub->start_display_time, ffsub->end_display_time);
334
0
    spu->i_start    = pts + VLC_TICK_FROM_MS(ffsub->start_display_time);
335
0
    spu->i_stop     = pts + VLC_TICK_FROM_MS(ffsub->end_display_time);
336
0
    spu->b_ephemer  = p_sys->b_need_ephemer;
337
                    /* We only show subtitle for i_stop time only */
338
339
0
    if (avctx->coded_width > 0 && avctx->coded_height > 0) {
340
0
        spu->i_original_picture_width = avctx->coded_width;
341
0
        spu->i_original_picture_height = avctx->coded_height;
342
0
    } else {
343
0
        spu->i_original_picture_width =
344
0
            dec->fmt_in->subs.spu.i_original_frame_width;
345
0
        spu->i_original_picture_height =
346
0
            dec->fmt_in->subs.spu.i_original_frame_height;
347
0
    }
348
349
0
    for (unsigned i = 0; i < ffsub->num_rects; i++) {
350
0
        AVSubtitleRect *rec = ffsub->rects[i];
351
352
        //msg_Err(dec, "SUBS RECT[%d]: %dx%d @%dx%d",
353
        //         i, rec->w, rec->h, rec->x, rec->y);
354
355
0
        subpicture_region_t *region = NULL;
356
0
        switch (ffsub->format) {
357
0
        case 0:
358
0
            region = ConvertRegionRGBA(rec);
359
0
            break;
360
0
        default:
361
0
            msg_Warn(dec, "unsupported subtitle type %" PRIu16, ffsub->format);
362
0
            break;
363
0
        }
364
0
        if (region) {
365
0
            vlc_spu_regions_push(&spu->regions, region);
366
0
        }
367
0
    }
368
0
    avsubtitle_free(ffsub);
369
370
0
    return spu;
371
0
}