/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 = ®ion->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 | } |