]> git.sesse.net Git - vlc/blob - modules/codec/avcodec/subtitle.c
Merge branch 'master' into lpcm_encoder
[vlc] / modules / codec / avcodec / subtitle.c
1 /*****************************************************************************
2  * subtitle.c: subtitle decoder using ffmpeg library
3  *****************************************************************************
4  * Copyright (C) 2009 Laurent Aimar
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30 #include <assert.h>
31
32 #include <vlc_common.h>
33 #include <vlc_codec.h>
34 #include <vlc_avcodec.h>
35
36 /* ffmpeg header */
37 #ifdef HAVE_LIBAVCODEC_AVCODEC_H
38 #   include <libavcodec/avcodec.h>
39 #   ifdef HAVE_AVCODEC_VAAPI
40 #       include <libavcodec/vaapi.h>
41 #   endif
42 #elif defined(HAVE_FFMPEG_AVCODEC_H)
43 #   include <ffmpeg/avcodec.h>
44 #else
45 #   include <avcodec.h>
46 #endif
47
48 #include "avcodec.h"
49
50 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT( 52, 25, 0 )
51
52 struct decoder_sys_t {
53     FFMPEG_COMMON_MEMBERS
54 };
55
56 static subpicture_t *ConvertSubtitle(decoder_t *, AVSubtitle *, mtime_t pts);
57
58 /**
59  * Initialize subtitle decoder
60  */
61 int InitSubtitleDec(decoder_t *dec, AVCodecContext *context,
62                     AVCodec *codec, int codec_id, const char *namecodec)
63 {
64     decoder_sys_t *sys;
65
66     /* */
67     switch (codec_id) {
68     case CODEC_ID_HDMV_PGS_SUBTITLE:
69     case CODEC_ID_XSUB:
70         break;
71     default:
72         msg_Warn(dec, "refusing to decode non validated subtitle codec");
73         return VLC_EGENERIC;
74     }
75
76     /* */
77     dec->p_sys = sys = malloc(sizeof(*sys));
78     if (!sys)
79         return VLC_ENOMEM;
80
81     codec->type = CODEC_TYPE_SUBTITLE;
82     context->codec_type = CODEC_TYPE_SUBTITLE;
83     context->codec_id = codec_id;
84     sys->p_context = context;
85     sys->p_codec = codec;
86     sys->i_codec_id = codec_id;
87     sys->psz_namecodec = namecodec;
88     sys->b_delayed_open = false;
89
90     /* */
91     context->extradata_size = 0;
92     context->extradata = NULL;
93
94     /* */
95     vlc_avcodec_lock();
96     if (avcodec_open(context, codec) < 0) {
97         vlc_avcodec_unlock();
98         msg_Err(dec, "cannot open codec (%s)", namecodec);
99         free(context->extradata);
100         free(sys);
101         return VLC_EGENERIC;
102     }
103     vlc_avcodec_unlock();
104
105     /* */
106     msg_Dbg(dec, "ffmpeg codec (%s) started", namecodec);
107     dec->fmt_out.i_cat = SPU_ES;
108
109     return VLC_SUCCESS;
110 }
111
112 /**
113  * Decode one subtitle
114  */
115 subpicture_t *DecodeSubtitle(decoder_t *dec, block_t **block_ptr)
116 {
117     decoder_sys_t *sys = dec->p_sys;
118
119     if (!block_ptr || !*block_ptr)
120         return NULL;
121
122     block_t *block = *block_ptr;
123
124     if (block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED)) {
125         block_Release(block);
126         avcodec_flush_buffers(sys->p_context);
127         return NULL;
128     }
129
130     if (block->i_buffer <= 0) {
131         block_Release(block);
132         return NULL;
133     }
134
135     *block_ptr =
136     block      = block_Realloc(block,
137                                0,
138                                block->i_buffer + FF_INPUT_BUFFER_PADDING_SIZE);
139     if (!block)
140         return NULL;
141     block->i_buffer -= FF_INPUT_BUFFER_PADDING_SIZE;
142     memset(&block->p_buffer[block->i_buffer], 0, FF_INPUT_BUFFER_PADDING_SIZE);
143
144     /* */
145     AVSubtitle subtitle;
146     memset(&subtitle, 0, sizeof(subtitle));
147
148     AVPacket pkt;
149     av_init_packet(&pkt);
150     pkt.data = block->p_buffer;
151     pkt.size = block->i_buffer;
152
153     int has_subtitle = 0;
154     int used = avcodec_decode_subtitle2(sys->p_context,
155                                         &subtitle, &has_subtitle, &pkt);
156
157     if (used < 0) {
158         msg_Warn(dec, "cannot decode one subtitle (%zu bytes)",
159                  block->i_buffer);
160
161         block_Release(block);
162         return NULL;
163     } else if ((size_t)used > block->i_buffer) {
164         used = block->i_buffer;
165     }
166
167     block->i_buffer -= used;
168     block->p_buffer += used;
169
170     /* */
171     subpicture_t *spu = NULL;
172     if (has_subtitle)
173         spu = ConvertSubtitle(dec, &subtitle,
174                               block->i_pts > 0 ? block->i_pts : block->i_dts);
175
176     /* */
177     if (!spu)
178         block_Release(block);
179     return spu;
180 }
181
182 /**
183  * Clean up private data
184  */
185 void EndSubtitleDec(decoder_t *dec)
186 {
187     VLC_UNUSED(dec);
188 }
189
190 /**
191  * Convert a RGBA ffmpeg region to our format.
192  */
193 static subpicture_region_t *ConvertRegionRGBA(AVSubtitleRect *ffregion)
194 {
195     if (ffregion->w <= 0 || ffregion->h <= 0)
196         return NULL;
197
198     video_format_t fmt;
199     memset(&fmt, 0, sizeof(fmt));
200     fmt.i_chroma         = VLC_FOURCC('R','G','B','A');
201     fmt.i_width          =
202     fmt.i_visible_width  = ffregion->w;
203     fmt.i_height         =
204     fmt.i_visible_height = ffregion->h;
205     fmt.i_x_offset       = 0;
206     fmt.i_y_offset       = 0;
207
208     subpicture_region_t *region = subpicture_region_New(&fmt);
209     if (!region)
210         return NULL;
211
212     region->i_x = ffregion->x;
213     region->i_y = ffregion->y;
214     region->i_align = SUBPICTURE_ALIGN_TOP | SUBPICTURE_ALIGN_LEFT;
215
216     const plane_t *p = &region->p_picture->p[0];
217     for (int y = 0; y < ffregion->h; y++) {
218         for (int x = 0; x < ffregion->w; x++) {
219             /* I don't think don't have paletized RGB_A_ */
220             const uint8_t index = ffregion->pict.data[0][y * ffregion->w+x];
221             assert(index < ffregion->nb_colors);
222
223             uint32_t color;
224             memcpy(&color, &ffregion->pict.data[1][4*index], 4);
225
226             uint8_t *p_rgba = &p->p_pixels[y * p->i_pitch + x * p->i_pixel_pitch];
227             p_rgba[0] = (color >> 16) & 0xff;
228             p_rgba[1] = (color >>  8) & 0xff;
229             p_rgba[2] = (color >>  0) & 0xff;
230             p_rgba[3] = (color >> 24) & 0xff;
231         }
232     }
233
234     return region;
235 }
236
237 /**
238  * Convert a ffmpeg subtitle to our format.
239  */
240 static subpicture_t *ConvertSubtitle(decoder_t *dec, AVSubtitle *ffsub, mtime_t pts)
241 {
242     subpicture_t *spu = decoder_NewSubpicture(dec, NULL);
243     if (!spu)
244         return NULL;
245
246     //msg_Err(dec, "%lld %d %d",
247     //        pts, ffsub->start_display_time, ffsub->end_display_time);
248     spu->i_start    = pts + ffsub->start_display_time * INT64_C(1000);
249     spu->i_stop     = pts + ffsub->end_display_time * INT64_C(1000);
250     spu->b_absolute = true; /* FIXME How to set it right ? */
251     spu->b_ephemer  = true; /* FIXME How to set it right ? */
252     spu->i_original_picture_width =
253         dec->fmt_in.subs.spu.i_original_frame_width;
254     spu->i_original_picture_height =
255         dec->fmt_in.subs.spu.i_original_frame_height;
256
257     subpicture_region_t **region_next = &spu->p_region;
258
259     for (unsigned i = 0; i < ffsub->num_rects; i++) {
260         AVSubtitleRect *rec = ffsub->rects[i];
261
262         //msg_Err(dec, "SUBS RECT[%d]: %dx%d @%dx%d",
263         //         i, rec->w, rec->h, rec->x, rec->y);
264
265         subpicture_region_t *region = NULL;
266         switch (ffsub->format) {
267         case 0:
268             region = ConvertRegionRGBA(rec);
269             break;
270         default:
271             msg_Warn(dec, "unsupported subtitle type");
272             region = NULL;
273             break;
274         }
275         if (region) {
276             *region_next = region;
277             region_next = &region->p_next;
278         }
279         /* Free AVSubtitleRect */
280         avpicture_free(&rec->pict);
281         av_free(rec);
282     }
283     av_free(ffsub->rects);
284
285     return spu;
286 }
287
288 #endif