/src/vlc/modules/codec/textst.c
Line | Count | Source (jump to first uncovered line) |
1 | | /***************************************************************************** |
2 | | * textst.c: HDMV TextST subtitles decoder |
3 | | ***************************************************************************** |
4 | | * Copyright (C) 2017 Videolan Authors |
5 | | * |
6 | | * Adapted from libluray textst_decode.c |
7 | | * Copyright (C) 2013 Petri Hintukainen <phintuka@users.sourceforge.net> |
8 | | * |
9 | | * This program is free software; you can redistribute it and/or modify it |
10 | | * under the terms of the GNU Lesser General Public License as published by |
11 | | * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. |
18 | | * |
19 | | * You should have received a copy of the GNU Lesser General Public License |
20 | | * along with this program; if not, write to the Free Software Foundation, |
21 | | * 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 | | |
31 | | #include <vlc_common.h> |
32 | | #include <vlc_plugin.h> |
33 | | #include <vlc_codec.h> |
34 | | |
35 | | #include "../demux/mpeg/timestamps.h" |
36 | | |
37 | | #include "substext.h" |
38 | | |
39 | | /***************************************************************************** |
40 | | * Module descriptor |
41 | | *****************************************************************************/ |
42 | | static int Open (vlc_object_t *); |
43 | | |
44 | | typedef struct |
45 | | { |
46 | | uint32_t palette[256]; |
47 | | } decoder_sys_t; |
48 | | |
49 | 0 | vlc_module_begin() |
50 | 0 | set_description(N_("HDMV TextST subtitles decoder")) |
51 | 0 | set_subcategory(SUBCAT_INPUT_SCODEC) |
52 | 0 | set_capability("spu decoder", 10) |
53 | 0 | set_callback(Open) |
54 | 0 | vlc_module_end() |
55 | | |
56 | 0 | #define BD_TEXTST_DATA_STRING 1 |
57 | 0 | #define BD_TEXTST_DATA_FONT_ID 2 |
58 | 0 | #define BD_TEXTST_DATA_FONT_STYLE 3 |
59 | 0 | #define BD_TEXTST_DATA_FONT_SIZE 4 |
60 | 0 | #define BD_TEXTST_DATA_FONT_COLOR 5 |
61 | 0 | #define BD_TEXTST_DATA_NEWLINE 0x0a |
62 | 0 | #define BD_TEXTST_DATA_RESET_STYLE 0x0b |
63 | | |
64 | | static size_t textst_FillRegion(decoder_t *p_dec, const uint8_t *p_data, size_t i_data, |
65 | | substext_updater_region_t *p_region) |
66 | 0 | { |
67 | 0 | decoder_sys_t *p_sys = p_dec->p_sys; |
68 | 0 | text_segment_t **pp_last = &p_region->p_segments; |
69 | 0 | text_style_t *p_style = NULL; |
70 | | |
71 | | /* p_data[0] */ |
72 | | /* continous_present_flag b1 */ |
73 | | /* forced_on_flag b1 */ |
74 | | /* ? b6 */ |
75 | |
|
76 | 0 | assert( i_data >= 4 ); |
77 | | |
78 | | //uint8_t region_style_id_ref = p_data[1]; |
79 | 0 | uint16_t i_data_length = GetWBE(&p_data[2]); |
80 | |
|
81 | 0 | p_data += 4; i_data -= 4; |
82 | 0 | if( i_data < i_data_length ) |
83 | 0 | return i_data; |
84 | 0 | else |
85 | 0 | i_data = i_data_length; |
86 | | |
87 | 0 | while (i_data > 3) |
88 | 0 | { |
89 | | /* parse header */ |
90 | 0 | uint8_t code = p_data[0]; |
91 | 0 | if (code != 0x1b) { |
92 | 0 | p_data++; i_data--; |
93 | 0 | continue; |
94 | 0 | } |
95 | | |
96 | 0 | uint8_t type = p_data[1]; |
97 | 0 | uint8_t length = p_data[2]; |
98 | |
|
99 | 0 | p_data += 3; i_data -= 3; |
100 | |
|
101 | 0 | if(length > i_data) |
102 | 0 | break; |
103 | | |
104 | 0 | switch (type) |
105 | 0 | { |
106 | 0 | case BD_TEXTST_DATA_STRING: |
107 | 0 | { |
108 | 0 | char *psz = strndup((char *)p_data, length); |
109 | 0 | *pp_last = text_segment_New(psz); |
110 | 0 | free(psz); |
111 | 0 | if(p_style && *pp_last) |
112 | 0 | (*pp_last)->style = text_style_Duplicate(p_style); |
113 | 0 | } |
114 | 0 | break; |
115 | 0 | case BD_TEXTST_DATA_FONT_ID: |
116 | | //p_data[0] font_id; |
117 | 0 | break; |
118 | 0 | case BD_TEXTST_DATA_FONT_STYLE: |
119 | 0 | if(i_data > 2 && (p_style || (p_style = text_style_Create( STYLE_NO_DEFAULTS )))) |
120 | 0 | { |
121 | 0 | if(p_data[0] & 0x01) |
122 | 0 | p_style->i_style_flags |= STYLE_BOLD; |
123 | 0 | if(p_data[0] & 0x02) |
124 | 0 | p_style->i_style_flags |= STYLE_ITALIC; |
125 | 0 | if(p_data[0] & 0x04) |
126 | 0 | p_style->i_style_flags |= STYLE_OUTLINE; |
127 | 0 | p_style->i_outline_color = p_sys->palette[p_data[1]] & 0x00FFFFFF; |
128 | 0 | p_style->i_outline_alpha = p_sys->palette[p_data[1]] >> 24; |
129 | 0 | p_style->i_features |= STYLE_HAS_FLAGS | STYLE_HAS_OUTLINE_ALPHA | STYLE_HAS_OUTLINE_COLOR; |
130 | | //p_data[2] outline__thickness |
131 | 0 | } |
132 | 0 | break; |
133 | 0 | case BD_TEXTST_DATA_FONT_SIZE: |
134 | | /*if(i_data > 0) |
135 | | p_style->f_font_relsize = STYLE_DEFAULT_REL_FONT_SIZE * |
136 | | (p_data[0] << 4) / STYLE_DEFAULT_FONT_SIZE;*/ |
137 | 0 | break; |
138 | 0 | case BD_TEXTST_DATA_FONT_COLOR: |
139 | 0 | if(i_data > 1 && (p_style || (p_style = text_style_Create( STYLE_NO_DEFAULTS )))) |
140 | 0 | { |
141 | 0 | p_style->i_font_color = p_sys->palette[p_data[1]] & 0x00FFFFFF; |
142 | 0 | p_style->i_font_alpha = p_sys->palette[p_data[1]] >> 24; |
143 | 0 | p_style->i_features |= STYLE_HAS_FONT_ALPHA | STYLE_HAS_FONT_COLOR; |
144 | 0 | } |
145 | 0 | break; |
146 | 0 | case BD_TEXTST_DATA_NEWLINE: |
147 | 0 | *pp_last = text_segment_New("\n"); |
148 | 0 | break; |
149 | 0 | case BD_TEXTST_DATA_RESET_STYLE: |
150 | 0 | if(p_style) |
151 | 0 | { |
152 | 0 | text_style_Delete(p_style); |
153 | 0 | p_style = NULL; |
154 | 0 | } |
155 | 0 | break; |
156 | 0 | default: |
157 | 0 | break; |
158 | 0 | } |
159 | | |
160 | 0 | if(*pp_last) |
161 | 0 | pp_last = &(*pp_last)->p_next; |
162 | |
|
163 | 0 | p_data += length; i_data -= length; |
164 | 0 | } |
165 | | |
166 | 0 | if(p_style) |
167 | 0 | text_style_Delete(p_style); |
168 | |
|
169 | 0 | return i_data_length; |
170 | 0 | } |
171 | | |
172 | | static size_t textst_Decode_palette(decoder_t *p_dec, const uint8_t *p_data, size_t i_data) |
173 | 0 | { |
174 | 0 | if(i_data < 2) |
175 | 0 | return i_data; |
176 | | |
177 | 0 | decoder_sys_t *p_sys = p_dec->p_sys; |
178 | |
|
179 | 0 | uint16_t i_size = GetWBE(&p_data[0]); |
180 | 0 | p_data += 2; i_data -= 2; |
181 | |
|
182 | 0 | i_size = i_data = __MIN(i_data, i_size); |
183 | 0 | while (i_data > 4) |
184 | 0 | { |
185 | 0 | p_sys->palette[p_data[0]] = /* YCrCbT to ARGB */ |
186 | 0 | ( (uint32_t)((float)p_data[1] +1.402f * (p_data[2]-128)) << 16 ) | |
187 | 0 | ( (uint32_t)((float)p_data[1] -0.34414 * (p_data[3]-128) -0.71414 * (p_data[2]-128)) << 8 ) | |
188 | 0 | ( (uint32_t)((float)p_data[1] +1.722 * (p_data[3]-128)) ) | |
189 | 0 | ( (0xFF - p_data[4]) << 24 ); |
190 | 0 | p_data += 5; i_data -= 5; |
191 | 0 | } |
192 | |
|
193 | 0 | return i_size; |
194 | 0 | } |
195 | | |
196 | | static void textst_FillRegions(decoder_t *p_dec, const uint8_t *p_data, size_t i_data, |
197 | | substext_updater_region_t *p_region) |
198 | 0 | { |
199 | 0 | substext_updater_region_t **pp_last = &p_region; |
200 | 0 | bool palette_update_flag = p_data[0] >> 7; |
201 | 0 | p_data++; i_data--; |
202 | |
|
203 | 0 | if (palette_update_flag) |
204 | 0 | { |
205 | 0 | size_t i_read = textst_Decode_palette(p_dec, p_data, i_data); |
206 | 0 | p_data += i_read; i_data -= i_read; |
207 | 0 | } |
208 | |
|
209 | 0 | if(i_data > 2) |
210 | 0 | { |
211 | 0 | uint8_t i_region_count = p_data[0]; |
212 | 0 | p_data++; i_data--; |
213 | |
|
214 | 0 | for(uint8_t i=0; i<i_region_count && i_data > 4; i++) |
215 | 0 | { |
216 | 0 | if(*pp_last == NULL) |
217 | 0 | { |
218 | 0 | *pp_last = SubpictureUpdaterSysRegionNew(); |
219 | 0 | if(!*pp_last) |
220 | 0 | break; |
221 | 0 | } |
222 | 0 | size_t i_read = textst_FillRegion(p_dec, p_data, i_data, *pp_last); |
223 | 0 | (*pp_last)->align = SUBPICTURE_ALIGN_BOTTOM; |
224 | 0 | pp_last = &(*pp_last)->p_next; |
225 | 0 | p_data += i_read; i_data -= i_read; |
226 | 0 | } |
227 | 0 | } |
228 | 0 | } |
229 | | |
230 | | static int Decode(decoder_t *p_dec, block_t *p_block) |
231 | 0 | { |
232 | 0 | subpicture_t *p_sub = NULL; |
233 | 0 | if (p_block == NULL) /* No Drain */ |
234 | 0 | return VLCDEC_SUCCESS; |
235 | | |
236 | 0 | if (p_block->i_buffer > 18 && |
237 | 0 | (p_block->i_flags & BLOCK_FLAG_CORRUPTED) == 0 && |
238 | 0 | (p_sub = decoder_NewSubpictureText(p_dec))) |
239 | 0 | { |
240 | 0 | p_sub->i_start = FROM_SCALE(((int64_t)(p_block->p_buffer[3] & 0x01) << 32) | GetDWBE(&p_block->p_buffer[4])); |
241 | 0 | p_sub->i_stop = FROM_SCALE(((int64_t)(p_block->p_buffer[8] & 0x01) << 32) | GetDWBE(&p_block->p_buffer[9])); |
242 | 0 | if (p_sub->i_start < p_block->i_dts) |
243 | 0 | { |
244 | 0 | p_sub->i_stop += p_block->i_dts - p_sub->i_start; |
245 | 0 | p_sub->i_start = p_block->i_dts; |
246 | 0 | } |
247 | |
|
248 | 0 | subtext_updater_sys_t *p_spusys = p_sub->updater.p_sys; |
249 | 0 | textst_FillRegions(p_dec, &p_block->p_buffer[13], p_block->i_buffer - 13, |
250 | 0 | &p_spusys->region); |
251 | |
|
252 | 0 | p_sub->b_absolute = false; |
253 | 0 | decoder_QueueSub(p_dec, p_sub); |
254 | 0 | } |
255 | |
|
256 | 0 | block_Release(p_block); |
257 | 0 | return VLCDEC_SUCCESS; |
258 | 0 | } |
259 | | |
260 | | static int Open(vlc_object_t *object) |
261 | 0 | { |
262 | 0 | decoder_t *p_dec = (decoder_t*)object; |
263 | |
|
264 | 0 | if (p_dec->fmt_in->i_codec != VLC_CODEC_BD_TEXT) |
265 | 0 | return VLC_EGENERIC; |
266 | | |
267 | 0 | decoder_sys_t *p_sys = vlc_obj_malloc(object, sizeof(decoder_sys_t)); |
268 | 0 | if(!p_sys) |
269 | 0 | return VLC_ENOMEM; |
270 | 0 | memset(p_sys->palette, 0xFF, 256 * sizeof(uint32_t)); |
271 | |
|
272 | 0 | p_dec->p_sys = p_sys; |
273 | 0 | p_dec->pf_decode = Decode; |
274 | 0 | p_dec->fmt_out.i_codec = 0; |
275 | |
|
276 | 0 | return VLC_SUCCESS; |
277 | 0 | } |
278 | | |