/src/vlc/modules/codec/spudec/parse.c
Line | Count | Source (jump to first uncovered line) |
1 | | /***************************************************************************** |
2 | | * parse.c: SPU parser |
3 | | ***************************************************************************** |
4 | | * Copyright (C) 2000-2001, 2005, 2006 VLC authors and VideoLAN |
5 | | * |
6 | | * Authors: Sam Hocevar <sam@zoy.org> |
7 | | * Laurent Aimar <fenrir@via.ecp.fr> |
8 | | * Gildas Bazin <gbazin@videolan.org> |
9 | | * |
10 | | * This program is free software; you can redistribute it and/or modify it |
11 | | * under the terms of the GNU Lesser General Public License as published by |
12 | | * the Free Software Foundation; either version 2.1 of the License, or |
13 | | * (at your option) any later version. |
14 | | * |
15 | | * This program is distributed in the hope that it will be useful, |
16 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | | * GNU Lesser General Public License for more details. |
19 | | * |
20 | | * You should have received a copy of the GNU Lesser General Public License |
21 | | * along with this program; if not, write to the Free Software Foundation, |
22 | | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
23 | | *****************************************************************************/ |
24 | | |
25 | | /***************************************************************************** |
26 | | * Preamble |
27 | | *****************************************************************************/ |
28 | | #ifdef HAVE_CONFIG_H |
29 | | # include "config.h" |
30 | | #endif |
31 | | |
32 | | #include <vlc_common.h> |
33 | | #include <vlc_codec.h> |
34 | | |
35 | | #include "spudec.h" |
36 | | |
37 | | /***************************************************************************** |
38 | | * Local prototypes. |
39 | | *****************************************************************************/ |
40 | | typedef struct |
41 | | { |
42 | | int i_width; |
43 | | int i_height; |
44 | | int i_x; |
45 | | int i_y; |
46 | | vlc_tick_t i_start; |
47 | | vlc_tick_t i_stop; |
48 | | bool b_ephemer; |
49 | | bool b_subtitle; |
50 | | } spu_properties_t; |
51 | | |
52 | | typedef struct |
53 | | { |
54 | | int pi_offset[2]; /* byte offsets to data */ |
55 | | |
56 | | /* Color information */ |
57 | | bool b_palette; |
58 | | uint8_t pi_alpha[4]; |
59 | | uint8_t pi_yuv[4][3]; |
60 | | /* PXCTLI commands storage */ |
61 | | const uint8_t *p_pxctli; |
62 | | size_t i_pxclti; |
63 | | |
64 | | /* Auto crop fullscreen subtitles */ |
65 | | bool b_auto_crop; |
66 | | int i_y_top_offset; |
67 | | int i_y_bottom_offset; |
68 | | |
69 | | } subpicture_data_t; |
70 | | |
71 | | static int ParseControlSeq( decoder_t *, vlc_tick_t i_pts, |
72 | | void(*pf_queue)(decoder_t *, subpicture_t *) ); |
73 | | static int ParseRLE ( decoder_t *, subpicture_data_t *, |
74 | | const spu_properties_t *, uint16_t * ); |
75 | | static int Render ( decoder_t *, subpicture_t *, const uint16_t *, |
76 | | const subpicture_data_t *, const spu_properties_t * ); |
77 | | |
78 | | /***************************************************************************** |
79 | | * AddNibble: read a nibble from a source packet and add it to our integer. |
80 | | *****************************************************************************/ |
81 | | static inline unsigned int AddNibble( unsigned int i_code, |
82 | | const uint8_t *p_src, unsigned int *pi_index ) |
83 | 0 | { |
84 | 0 | if( *pi_index & 0x1 ) |
85 | 0 | { |
86 | 0 | return( i_code << 4 | ( p_src[(*pi_index)++ >> 1] & 0xf ) ); |
87 | 0 | } |
88 | 0 | else |
89 | 0 | { |
90 | 0 | return( i_code << 4 | p_src[(*pi_index)++ >> 1] >> 4 ); |
91 | 0 | } |
92 | 0 | } |
93 | | |
94 | | static void CLUTIdxToYUV(const struct subs_format_t *subs, |
95 | | const uint8_t idx[4], uint8_t yuv[4][3]) |
96 | 0 | { |
97 | 0 | for( int i = 0; i < 4 ; i++ ) |
98 | 0 | { |
99 | 0 | uint32_t i_ayvu = subs->spu.palette[idx[i]]; |
100 | | /* FIXME: this job should be done sooner */ |
101 | 0 | yuv[3-i][0] = i_ayvu>>16; |
102 | 0 | yuv[3-i][1] = i_ayvu; |
103 | 0 | yuv[3-i][2] = i_ayvu>>8; |
104 | 0 | } |
105 | 0 | } |
106 | | |
107 | | static void ParsePXCTLI( decoder_t *p_dec, const subpicture_data_t *p_spu_data, |
108 | | subpicture_region_t *p_region ) |
109 | 0 | { |
110 | 0 | plane_t *p_plane = &p_region->p_picture->p[0]; |
111 | 0 | video_palette_t *p_palette = p_region->p_picture->format.p_palette; |
112 | |
|
113 | 0 | if( !p_dec->fmt_in->subs.spu.b_palette ) |
114 | 0 | return; |
115 | | |
116 | 0 | for( size_t i=0;i<p_spu_data->i_pxclti; i++ ) |
117 | 0 | { |
118 | 0 | uint16_t i_col = GetWBE(&p_spu_data->p_pxctli[i*6 + 0]); |
119 | 0 | uint16_t i_color = GetWBE(&p_spu_data->p_pxctli[i*6 + 2]); |
120 | 0 | uint16_t i_contrast = GetWBE(&p_spu_data->p_pxctli[i*6 + 4]); |
121 | | |
122 | | /* Lookup the CLUT palette for the YUV values */ |
123 | 0 | uint8_t idx[4]; |
124 | 0 | uint8_t yuv[4][3]; |
125 | 0 | uint8_t alpha[4]; |
126 | 0 | idx[0] = (i_color >> 12)&0x0f; |
127 | 0 | idx[1] = (i_color >> 8)&0x0f; |
128 | 0 | idx[2] = (i_color >> 4)&0x0f; |
129 | 0 | idx[3] = i_color&0x0f; |
130 | 0 | CLUTIdxToYUV( &p_dec->fmt_in->subs, idx, yuv ); |
131 | | |
132 | | /* Process the contrast */ |
133 | 0 | alpha[3] = (i_contrast >> 12)&0x0f; |
134 | 0 | alpha[2] = (i_contrast >> 8)&0x0f; |
135 | 0 | alpha[1] = (i_contrast >> 4)&0x0f; |
136 | 0 | alpha[0] = i_contrast&0x0f; |
137 | | |
138 | | /* Create a new YUVA palette entries for the picture */ |
139 | 0 | int index_map[4]; |
140 | 0 | for( int j=0; j<4; j++ ) |
141 | 0 | { |
142 | 0 | uint8_t yuvaentry[4]; |
143 | 0 | yuvaentry[0] = yuv[j][0]; |
144 | 0 | yuvaentry[1] = yuv[j][1]; |
145 | 0 | yuvaentry[2] = yuv[j][2]; |
146 | 0 | yuvaentry[3] = alpha[j] * 0x11; |
147 | 0 | int i_index = VIDEO_PALETTE_COLORS_MAX; |
148 | 0 | for( int k = p_palette->i_entries; k > 0; k-- ) |
149 | 0 | { |
150 | 0 | if( !memcmp( p_palette->palette[k], yuvaentry, sizeof(uint8_t [4]) ) ) |
151 | 0 | { |
152 | 0 | i_index = i; |
153 | 0 | break; |
154 | 0 | } |
155 | 0 | } |
156 | | |
157 | | /* Add an entry in out palette */ |
158 | 0 | if( i_index == VIDEO_PALETTE_COLORS_MAX ) |
159 | 0 | { |
160 | 0 | if(p_palette->i_entries == VIDEO_PALETTE_COLORS_MAX) |
161 | 0 | { |
162 | 0 | msg_Warn( p_dec, "Cannot create new color, skipping PXCTLI" ); |
163 | 0 | return; |
164 | 0 | } |
165 | 0 | i_index = p_palette->i_entries++; |
166 | 0 | memcpy( p_palette->palette[ i_index ], yuvaentry, sizeof(uint8_t [4]) ); |
167 | 0 | } |
168 | 0 | index_map[j] = i_index; |
169 | 0 | } |
170 | | |
171 | 0 | if( p_region->i_x >= i_col ) |
172 | 0 | i_col -= p_region->i_x; |
173 | |
|
174 | 0 | for( int j=0; j<p_plane->i_visible_lines; j++ ) |
175 | 0 | { |
176 | 0 | uint8_t *p_line = &p_plane->p_pixels[j * p_plane->i_pitch]; |
177 | | /* Extends to end of the line */ |
178 | 0 | for( int k=i_col; k<p_plane->i_visible_pitch; k++ ) |
179 | 0 | { |
180 | 0 | if( p_line[k] < 4 ) /* can forge write-again */ |
181 | 0 | p_line[k] = index_map[ p_line[k] ]; |
182 | 0 | } |
183 | 0 | } |
184 | 0 | } |
185 | 0 | } |
186 | | |
187 | | static void UpdateDvdSpu(subpicture_t * p_spu, |
188 | | const struct vlc_spu_updater_configuration * cfg) |
189 | 0 | { |
190 | 0 | p_spu->i_original_picture_width = cfg->video_src->i_visible_width; |
191 | 0 | p_spu->i_original_picture_height = cfg->video_src->i_visible_height; |
192 | 0 | } |
193 | | |
194 | | /***************************************************************************** |
195 | | * OutputPicture: |
196 | | ***************************************************************************** |
197 | | *****************************************************************************/ |
198 | | static void OutputPicture( decoder_t *p_dec, |
199 | | const subpicture_data_t *p_spu_data, |
200 | | const spu_properties_t *p_spu_properties, |
201 | | void(*pf_queue)(decoder_t *, subpicture_t *) ) |
202 | 1 | { |
203 | 1 | decoder_sys_t *p_sys = p_dec->p_sys; |
204 | 1 | subpicture_t *p_spu; |
205 | 1 | uint16_t *p_pixeldata; |
206 | | |
207 | | /* Allocate the subpicture internal data. */ |
208 | 1 | static const struct vlc_spu_updater_ops spu_ops = |
209 | 1 | { |
210 | 1 | .update = UpdateDvdSpu, |
211 | 1 | }; |
212 | | |
213 | 1 | static const subpicture_updater_t updater = { |
214 | 1 | .ops = &spu_ops, |
215 | 1 | }; |
216 | 1 | p_spu = decoder_NewSubpicture( p_dec, &updater ); |
217 | 1 | if( !p_spu ) return; |
218 | | |
219 | 1 | p_spu->i_original_picture_width = |
220 | 1 | p_dec->fmt_in->subs.spu.i_original_frame_width; |
221 | 1 | p_spu->i_original_picture_height = |
222 | 1 | p_dec->fmt_in->subs.spu.i_original_frame_height; |
223 | 1 | p_spu->i_start = p_spu_properties->i_start; |
224 | 1 | p_spu->i_stop = p_spu_properties->i_stop; |
225 | 1 | p_spu->b_ephemer = p_spu_properties->b_ephemer; |
226 | 1 | p_spu->b_subtitle = p_spu_properties->b_subtitle; |
227 | | |
228 | 1 | if( (p_spu->i_stop == VLC_TICK_INVALID || p_spu->i_stop <= p_spu->i_start) && |
229 | 1 | !p_spu->b_ephemer ) |
230 | 0 | { |
231 | | /* This subtitle will live for 5 seconds or until the next subtitle */ |
232 | 0 | p_spu->i_stop = p_spu->i_start + VLC_TICK_FROM_MS(500 * 11); |
233 | 0 | p_spu->b_ephemer = true; |
234 | 0 | } |
235 | | |
236 | | /* we are going to expand the RLE stuff so that we won't need to read |
237 | | * nibbles later on. This will speed things up a lot. Plus, we'll only |
238 | | * need to do this stupid interlacing stuff once. |
239 | | * |
240 | | * Rationale for the "p_spudec->i_rle_size * 4*sizeof(*spu_data.p_data)": |
241 | | * one byte gaves two nibbles and may be used twice (once per field) |
242 | | * generating 4 codes. |
243 | | */ |
244 | 1 | p_pixeldata = vlc_alloc( p_sys->i_rle_size, sizeof(*p_pixeldata) * 2 * 2 ); |
245 | | |
246 | | /* We try to display it */ |
247 | 1 | subpicture_data_t render_spu_data = *p_spu_data; /* Need a copy */ |
248 | 1 | if( ParseRLE( p_dec, &render_spu_data, p_spu_properties, p_pixeldata ) ) |
249 | 0 | { |
250 | | /* There was a parse error, delete the subpicture */ |
251 | 0 | subpicture_Delete( p_spu ); |
252 | 0 | free( p_pixeldata ); |
253 | 0 | return; |
254 | 0 | } |
255 | | |
256 | | #ifdef DEBUG_SPUDEC |
257 | | msg_Dbg( p_dec, "total size: 0x%x, RLE offsets: 0x%x 0x%x", |
258 | | p_sys->i_spu_size, |
259 | | render_spu_data.pi_offset[0], render_spu_data.pi_offset[1] ); |
260 | | #endif |
261 | | |
262 | 1 | if( Render( p_dec, p_spu, p_pixeldata, &render_spu_data, p_spu_properties ) ) |
263 | 0 | { |
264 | 0 | subpicture_Delete( p_spu ); |
265 | 0 | free( p_pixeldata ); |
266 | 0 | return; |
267 | 0 | } |
268 | | |
269 | 1 | free( p_pixeldata ); |
270 | | |
271 | 1 | if( p_spu_data->p_pxctli && p_spu ) |
272 | 0 | ParsePXCTLI( p_dec, p_spu_data, |
273 | 0 | vlc_spu_regions_first_or_null(&p_spu->regions) ); |
274 | | |
275 | 1 | pf_queue( p_dec, p_spu ); |
276 | 1 | } |
277 | | |
278 | | static int Validate( decoder_t *p_dec, unsigned i_index, |
279 | | unsigned i_cur_seq, unsigned i_next_seq, |
280 | | const subpicture_data_t *p_spu_data, |
281 | | const spu_properties_t *p_spu_properties ) |
282 | 72 | { |
283 | 72 | decoder_sys_t *p_sys = p_dec->p_sys; |
284 | | |
285 | | /* Check that the next sequence index matches the current one */ |
286 | 72 | if( i_next_seq < i_cur_seq ) |
287 | 21 | { |
288 | 21 | msg_Err( p_dec, "index mismatch (0x%.4x < 0x%.4x)", |
289 | 21 | i_next_seq, i_cur_seq ); |
290 | 21 | return VLC_EGENERIC; |
291 | 21 | } |
292 | | |
293 | 51 | if( i_index > p_sys->i_spu_size ) |
294 | 0 | { |
295 | 0 | msg_Err( p_dec, "uh-oh, we went too far (0x%.4x > 0x%.4x)", |
296 | 0 | i_index, p_sys->i_spu_size ); |
297 | 0 | return VLC_EGENERIC; |
298 | 0 | } |
299 | | |
300 | 51 | const int i_spu_size = p_sys->i_spu - 4; |
301 | 51 | if( p_spu_data->pi_offset[0] < 0 || p_spu_data->pi_offset[0] >= i_spu_size || |
302 | 51 | p_spu_data->pi_offset[1] < 0 || p_spu_data->pi_offset[1] >= i_spu_size ) |
303 | 50 | { |
304 | 50 | msg_Err( p_dec, "invalid offset values" ); |
305 | 50 | return VLC_EGENERIC; |
306 | 50 | } |
307 | | |
308 | 1 | if( p_spu_properties->i_start == VLC_TICK_INVALID ) |
309 | 0 | { |
310 | 0 | msg_Err( p_dec, "no `start display' command" ); |
311 | 0 | return VLC_EGENERIC; |
312 | 0 | } |
313 | | |
314 | | /* Get rid of padding bytes */ |
315 | 1 | if( i_index > i_next_seq && p_sys->i_spu_size > i_index + 1 ) |
316 | 1 | { |
317 | | /* Zero or one padding byte are quite usual |
318 | | * More than one padding byte - this is very strange, but |
319 | | * we can ignore them. */ |
320 | 1 | msg_Warn( p_dec, "%i padding bytes, we usually get 0 or 1 of them", |
321 | 1 | p_sys->i_spu_size - i_index ); |
322 | 1 | } |
323 | | |
324 | 1 | return VLC_SUCCESS; |
325 | 1 | } |
326 | | |
327 | | /***************************************************************************** |
328 | | * ParsePacket: parse an SPU packet and send it to the video output |
329 | | ***************************************************************************** |
330 | | * This function parses the SPU packet and, if valid, sends it to the |
331 | | * video output. |
332 | | *****************************************************************************/ |
333 | | void ParsePacket( decoder_t *p_dec, void(*pf_queue)(decoder_t *, subpicture_t *) ) |
334 | 6.56k | { |
335 | 6.56k | decoder_sys_t *p_sys = p_dec->p_sys; |
336 | | |
337 | | /* Getting the control part */ |
338 | 6.56k | if( ParseControlSeq( p_dec, p_sys->i_pts, pf_queue ) ) |
339 | 1.88k | return; |
340 | 6.56k | } |
341 | | |
342 | | /***************************************************************************** |
343 | | * ParseControlSeq: parse all SPU control sequences |
344 | | ***************************************************************************** |
345 | | * This is the most important part in SPU decoding. We get dates, palette |
346 | | * information, coordinates, and so on. For more information on the |
347 | | * subtitles format, see http://sam.zoy.org/doc/dvd/subtitles/index.html |
348 | | *****************************************************************************/ |
349 | | static int ParseControlSeq( decoder_t *p_dec, vlc_tick_t i_pts, |
350 | | void(*pf_queue)(decoder_t *, subpicture_t *) ) |
351 | 6.56k | { |
352 | 6.56k | decoder_sys_t *p_sys = p_dec->p_sys; |
353 | | |
354 | | /* Our current index in the SPU packet */ |
355 | 6.56k | unsigned int i_index; |
356 | | |
357 | | /* The next start-of-control-sequence index and the previous one */ |
358 | 6.56k | unsigned int i_next_seq = 0, i_cur_seq = 0; |
359 | | |
360 | | /* Command and date */ |
361 | 6.56k | uint8_t i_command = SPU_CMD_END; |
362 | 6.56k | vlc_tick_t date = 0; |
363 | 6.56k | bool b_cmd_offset = false; |
364 | 6.56k | bool b_cmd_alpha = false; |
365 | | |
366 | | /* Create working space for spu data */ |
367 | 6.56k | subpicture_data_t spu_data_cmd; |
368 | 6.56k | memset( &spu_data_cmd, 0, sizeof(spu_data_cmd) ); |
369 | 6.56k | spu_data_cmd.pi_offset[0] = -1; |
370 | 6.56k | spu_data_cmd.pi_offset[1] = -1; |
371 | 6.56k | spu_data_cmd.b_palette = false; |
372 | 6.56k | spu_data_cmd.b_auto_crop = false; |
373 | 6.56k | spu_data_cmd.i_y_top_offset = 0; |
374 | 6.56k | spu_data_cmd.i_y_bottom_offset = 0; |
375 | 6.56k | spu_data_cmd.pi_alpha[0] = 0x00; |
376 | 6.56k | spu_data_cmd.pi_alpha[1] = 0x0f; |
377 | 6.56k | spu_data_cmd.pi_alpha[2] = 0x0f; |
378 | 6.56k | spu_data_cmd.pi_alpha[3] = 0x0f; |
379 | | |
380 | 6.56k | subpicture_data_t spu_data = spu_data_cmd; |
381 | | |
382 | | /* Initialize the structure */ |
383 | 6.56k | spu_properties_t spu_properties; |
384 | 6.56k | memset( &spu_properties, 0, sizeof(spu_properties) ); |
385 | 6.56k | spu_properties.i_start = VLC_TICK_INVALID; |
386 | 6.56k | spu_properties.i_stop = VLC_TICK_INVALID; |
387 | 6.56k | spu_properties.b_subtitle = true; |
388 | | |
389 | 161k | for( i_index = 4 + p_sys->i_rle_size; i_index < p_sys->i_spu_size ; ) |
390 | 158k | { |
391 | | /* If we just read a command sequence, read the next one; |
392 | | * otherwise, go on with the commands of the current sequence. */ |
393 | 158k | if( i_command == SPU_CMD_END ) |
394 | 3.67k | { |
395 | 3.67k | if( i_index + 4 > p_sys->i_spu_size ) |
396 | 11 | { |
397 | 11 | msg_Err( p_dec, "overflow in SPU command sequence" ); |
398 | 11 | return VLC_EGENERIC; |
399 | 11 | } |
400 | | |
401 | | /* */ |
402 | 3.66k | b_cmd_offset = false; |
403 | 3.66k | b_cmd_alpha = false; |
404 | | /* Get the control sequence date */ |
405 | 3.66k | date = VLC_TICK_FROM_MS(GetWBE( &p_sys->buffer[i_index] ) * 11); |
406 | | |
407 | | /* Next offset */ |
408 | 3.66k | i_cur_seq = i_index; |
409 | 3.66k | i_next_seq = GetWBE( &p_sys->buffer[i_index+2] ); |
410 | | |
411 | 3.66k | if( i_next_seq > p_sys->i_spu_size ) |
412 | 461 | { |
413 | 461 | msg_Err( p_dec, "overflow in SPU next command sequence" ); |
414 | 461 | return VLC_EGENERIC; |
415 | 461 | } |
416 | | |
417 | | /* Skip what we just read */ |
418 | 3.19k | i_index += 4; |
419 | 3.19k | } |
420 | | |
421 | 158k | i_command = p_sys->buffer[i_index]; |
422 | | |
423 | 158k | switch( i_command ) |
424 | 158k | { |
425 | 10.3k | case SPU_CMD_FORCE_DISPLAY: /* 00 (force displaying) */ |
426 | 10.3k | spu_properties.i_start = i_pts + date; |
427 | 10.3k | spu_properties.b_ephemer = true; |
428 | | /* ignores picture date as display start time |
429 | | * works around non displayable (offset by few ms) |
430 | | * spu menu over still frame in SPU_Select */ |
431 | 10.3k | spu_properties.b_subtitle = false; |
432 | 10.3k | i_index += 1; |
433 | 10.3k | break; |
434 | | |
435 | | /* Convert the dates in seconds to PTS values */ |
436 | 1.12k | case SPU_CMD_START_DISPLAY: /* 01 (start displaying) */ |
437 | 1.12k | spu_properties.i_start = i_pts + date; |
438 | 1.12k | i_index += 1; |
439 | 1.12k | break; |
440 | | |
441 | 636 | case SPU_CMD_STOP_DISPLAY: /* 02 (stop displaying) */ |
442 | 636 | spu_properties.i_stop = i_pts + date; |
443 | 636 | i_index += 1; |
444 | 636 | break; |
445 | | |
446 | 52.7k | case SPU_CMD_SET_PALETTE: |
447 | | /* 03xxxx (palette) */ |
448 | 52.7k | if( i_index + 3 > p_sys->i_spu_size ) |
449 | 12 | { |
450 | 12 | msg_Err( p_dec, "overflow in SPU command" ); |
451 | 12 | return VLC_EGENERIC; |
452 | 12 | } |
453 | | |
454 | 52.7k | if( p_dec->fmt_in->subs.spu.b_palette ) |
455 | 0 | { |
456 | 0 | uint8_t idx[4]; |
457 | |
|
458 | 0 | spu_data_cmd.b_palette = true; |
459 | |
|
460 | 0 | idx[0] = (p_sys->buffer[i_index+1]>>4)&0x0f; |
461 | 0 | idx[1] = (p_sys->buffer[i_index+1])&0x0f; |
462 | 0 | idx[2] = (p_sys->buffer[i_index+2]>>4)&0x0f; |
463 | 0 | idx[3] = (p_sys->buffer[i_index+2])&0x0f; |
464 | |
|
465 | 0 | CLUTIdxToYUV( &p_dec->fmt_in->subs, idx, spu_data_cmd.pi_yuv ); |
466 | 0 | } |
467 | | |
468 | 52.7k | i_index += 3; |
469 | 52.7k | break; |
470 | | |
471 | 906 | case SPU_CMD_SET_ALPHACHANNEL: /* 04xxxx (alpha channel) */ |
472 | 906 | if( i_index + 3 > p_sys->i_spu_size ) |
473 | 1 | { |
474 | 1 | msg_Err( p_dec, "overflow in SPU command" ); |
475 | 1 | return VLC_EGENERIC; |
476 | 1 | } |
477 | | |
478 | 905 | if(!p_sys->b_disabletrans) |
479 | 905 | { /* If we want to use original transparency values */ |
480 | 905 | b_cmd_alpha = true; |
481 | 905 | spu_data_cmd.pi_alpha[3] = (p_sys->buffer[i_index+1]>>4)&0x0f; |
482 | 905 | spu_data_cmd.pi_alpha[2] = (p_sys->buffer[i_index+1])&0x0f; |
483 | 905 | spu_data_cmd.pi_alpha[1] = (p_sys->buffer[i_index+2]>>4)&0x0f; |
484 | 905 | spu_data_cmd.pi_alpha[0] = (p_sys->buffer[i_index+2])&0x0f; |
485 | 905 | } |
486 | | |
487 | 905 | i_index += 3; |
488 | 905 | break; |
489 | | |
490 | 89.4k | case SPU_CMD_SET_COORDINATES: /* 05xxxyyyxxxyyy (coordinates) */ |
491 | 89.4k | if( i_index + 7 > p_sys->i_spu_size ) |
492 | 34 | { |
493 | 34 | msg_Err( p_dec, "overflow in SPU command" ); |
494 | 34 | return VLC_EGENERIC; |
495 | 34 | } |
496 | | |
497 | 89.3k | spu_properties.i_x = (p_sys->buffer[i_index+1]<<4)| |
498 | 89.3k | ((p_sys->buffer[i_index+2]>>4)&0x0f); |
499 | 89.3k | spu_properties.i_width = (((p_sys->buffer[i_index+2]&0x0f)<<8)| |
500 | 89.3k | p_sys->buffer[i_index+3]) - spu_properties.i_x + 1; |
501 | | |
502 | 89.3k | spu_properties.i_y = (p_sys->buffer[i_index+4]<<4)| |
503 | 89.3k | ((p_sys->buffer[i_index+5]>>4)&0x0f); |
504 | 89.3k | spu_properties.i_height = (((p_sys->buffer[i_index+5]&0x0f)<<8)| |
505 | 89.3k | p_sys->buffer[i_index+6]) - spu_properties.i_y + 1; |
506 | 89.3k | if (spu_properties.i_width < 0 || spu_properties.i_height < 0) { |
507 | 128 | msg_Err( p_dec, "integer overflow in SPU command" ); |
508 | 128 | return VLC_EGENERIC; |
509 | 128 | } |
510 | | |
511 | | /* Auto crop fullscreen subtitles */ |
512 | 89.2k | if( spu_properties.i_height > 250 ) |
513 | 89.2k | spu_data.b_auto_crop = true; |
514 | | |
515 | 89.2k | i_index += 7; |
516 | 89.2k | break; |
517 | | |
518 | 79 | case SPU_CMD_SET_OFFSETS: /* 06xxxxyyyy (byte offsets) */ |
519 | 79 | if( i_index + 5 > p_sys->i_spu_size ) |
520 | 1 | { |
521 | 1 | msg_Err( p_dec, "overflow in SPU command" ); |
522 | 1 | return VLC_EGENERIC; |
523 | 1 | } |
524 | | |
525 | 78 | b_cmd_offset = true; |
526 | 78 | spu_data.pi_offset[0] = GetWBE(&p_sys->buffer[i_index+1]) - 4; |
527 | 78 | spu_data.pi_offset[1] = GetWBE(&p_sys->buffer[i_index+3]) - 4; |
528 | 78 | i_index += 5; |
529 | 78 | break; |
530 | | |
531 | 88 | case SPU_CMD_SET_COLCON: |
532 | 88 | if( i_index + 7 > p_sys->i_spu_size ) |
533 | 1 | { |
534 | 1 | msg_Err( p_dec, "overflow in SPU command" ); |
535 | 1 | return VLC_EGENERIC; |
536 | 1 | } |
537 | | |
538 | 87 | spu_properties.i_start = i_pts + date; |
539 | | |
540 | 87 | if( p_sys->i_spu_size > |
541 | 87 | i_index + 3 + 4 + (p_sys->buffer[i_index+5] >> 4) ) |
542 | 85 | { |
543 | 85 | spu_data.p_pxctli = &p_sys->buffer[i_index+3 + 4]; |
544 | 85 | spu_data.i_pxclti = p_sys->buffer[i_index+5] >> 4; |
545 | 85 | } |
546 | | |
547 | 87 | i_index += 1 + GetWBE(&p_sys->buffer[i_index+1]); |
548 | 87 | break; |
549 | | |
550 | 72 | case SPU_CMD_END: /* ff (end) */ |
551 | | |
552 | 72 | if( b_cmd_offset ) |
553 | 7 | { |
554 | | /* It seems that palette and alpha from the block having |
555 | | * the cmd offset have to be used |
556 | | * XXX is it all ? */ |
557 | 7 | spu_data.b_palette = spu_data_cmd.b_palette; |
558 | 7 | if( spu_data_cmd.b_palette ) |
559 | 0 | memcpy( spu_data.pi_yuv, spu_data_cmd.pi_yuv, sizeof(spu_data_cmd.pi_yuv) ); |
560 | 7 | if( b_cmd_alpha ) |
561 | 1 | memcpy( spu_data.pi_alpha, spu_data_cmd.pi_alpha, sizeof(spu_data_cmd.pi_alpha) ); |
562 | 7 | } |
563 | | |
564 | 72 | i_index += 1; |
565 | | |
566 | 72 | if( Validate( p_dec, i_index, i_cur_seq, i_next_seq, |
567 | 72 | &spu_data, &spu_properties ) == VLC_SUCCESS ) |
568 | 1 | OutputPicture( p_dec, &spu_data, &spu_properties, pf_queue ); |
569 | | |
570 | 72 | break; |
571 | | |
572 | 2.90k | default: /* xx (unknown command) */ |
573 | 2.90k | msg_Warn( p_dec, "unknown SPU command 0x%.2x", i_command ); |
574 | 2.90k | if( i_index + 1 < i_next_seq ) |
575 | 1.26k | { |
576 | | /* There is at least one other command sequence */ |
577 | 1.26k | if( p_sys->buffer[i_next_seq - 1] == SPU_CMD_END ) |
578 | 37 | { |
579 | | /* This is consistent. Skip to that command sequence. */ |
580 | 37 | i_index = i_next_seq; |
581 | 37 | } |
582 | 1.23k | else |
583 | 1.23k | { |
584 | | /* There were other commands. */ |
585 | 1.23k | msg_Warn( p_dec, "cannot recover, dropping subtitle" ); |
586 | 1.23k | return VLC_EGENERIC; |
587 | 1.23k | } |
588 | 1.26k | } |
589 | 1.63k | else |
590 | 1.63k | { |
591 | | /* We were in the last command sequence. Stop parsing by |
592 | | * pretending we met an SPU_CMD_END command. */ |
593 | 1.63k | i_command = SPU_CMD_END; |
594 | 1.63k | i_index++; |
595 | 1.63k | } |
596 | 158k | } |
597 | | |
598 | | /* */ |
599 | 156k | if( i_command == SPU_CMD_END && i_index != i_next_seq ) |
600 | 1.69k | break; |
601 | 156k | } |
602 | | |
603 | | /* Successfully parsed ! */ |
604 | | |
605 | 4.68k | return VLC_SUCCESS; |
606 | 6.56k | } |
607 | | |
608 | | /***************************************************************************** |
609 | | * ParseRLE: parse the RLE part of the subtitle |
610 | | ***************************************************************************** |
611 | | * This part parses the subtitle graphical data and stores it in a more |
612 | | * convenient structure for later decoding. For more information on the |
613 | | * subtitles format, see http://sam.zoy.org/doc/dvd/subtitles/index.html |
614 | | *****************************************************************************/ |
615 | | static int ParseRLE( decoder_t *p_dec, |
616 | | subpicture_data_t *p_spu_data, |
617 | | const spu_properties_t *p_spu_properties, |
618 | | uint16_t *p_pixeldata ) |
619 | 1 | { |
620 | 1 | decoder_sys_t *p_sys = p_dec->p_sys; |
621 | | |
622 | 1 | const unsigned int i_width = p_spu_properties->i_width; |
623 | 1 | const unsigned int i_height = p_spu_properties->i_height; |
624 | 1 | unsigned int i_x, i_y; |
625 | | |
626 | 1 | uint16_t *p_dest = p_pixeldata; |
627 | | |
628 | | /* The subtitles are interlaced, we need two offsets */ |
629 | 1 | unsigned int i_id = 0; /* Start on the even SPU layer */ |
630 | 1 | unsigned int pi_table[ 2 ]; |
631 | 1 | unsigned int *pi_offset; |
632 | | |
633 | | /* Cropping */ |
634 | 1 | bool b_empty_top = true; |
635 | 1 | unsigned int i_skipped_top = 0, i_skipped_bottom = 0; |
636 | 1 | unsigned int i_transparent_code = 0; |
637 | | |
638 | | /* Colormap statistics */ |
639 | 1 | int i_border = -1; |
640 | 1 | int stats[4]; stats[0] = stats[1] = stats[2] = stats[3] = 0; |
641 | | |
642 | 1 | pi_table[ 0 ] = p_spu_data->pi_offset[ 0 ] << 1; |
643 | 1 | pi_table[ 1 ] = p_spu_data->pi_offset[ 1 ] << 1; |
644 | | |
645 | 1 | for( i_y = 0 ; i_y < i_height ; i_y++ ) |
646 | 0 | { |
647 | 0 | unsigned int i_code; |
648 | 0 | pi_offset = pi_table + i_id; |
649 | |
|
650 | 0 | for( i_x = 0 ; i_x < i_width ; i_x += i_code >> 2 ) |
651 | 0 | { |
652 | 0 | i_code = 0; |
653 | 0 | for( unsigned int i_min = 1; i_min <= 0x40 && i_code < i_min; i_min <<= 2 ) |
654 | 0 | { |
655 | 0 | if( (*pi_offset >> 1) >= p_sys->i_spu_size ) |
656 | 0 | { |
657 | 0 | msg_Err( p_dec, "out of bounds while reading rle" ); |
658 | 0 | return VLC_EGENERIC; |
659 | 0 | } |
660 | 0 | i_code = AddNibble( i_code, &p_sys->buffer[4], pi_offset ); |
661 | 0 | } |
662 | 0 | if( i_code < 0x0004 ) |
663 | 0 | { |
664 | | /* If the 14 first bits are set to 0, then it's a |
665 | | * new line. We emulate it. */ |
666 | 0 | i_code |= ( i_width - i_x ) << 2; |
667 | 0 | } |
668 | |
|
669 | 0 | if( ( (i_code >> 2) + i_x + i_y * i_width ) > i_height * i_width ) |
670 | 0 | { |
671 | 0 | msg_Err( p_dec, "out of bounds, %i at (%i,%i) is out of %ix%i", |
672 | 0 | i_code >> 2, i_x, i_y, i_width, i_height ); |
673 | 0 | return VLC_EGENERIC; |
674 | 0 | } |
675 | | |
676 | | /* Try to find the border color */ |
677 | 0 | if( p_spu_data->pi_alpha[ i_code & 0x3 ] != 0x00 ) |
678 | 0 | { |
679 | 0 | i_border = i_code & 0x3; |
680 | 0 | stats[i_border] += i_code >> 2; |
681 | 0 | } |
682 | | |
683 | | /* Auto crop subtitles (a lot more optimized) */ |
684 | 0 | if( p_spu_data->b_auto_crop ) |
685 | 0 | { |
686 | 0 | if( !i_y ) |
687 | 0 | { |
688 | | /* We assume that if the first line is transparent, then |
689 | | * it is using the palette index for the |
690 | | * (background) transparent color */ |
691 | 0 | if( (i_code >> 2) == i_width && |
692 | 0 | p_spu_data->pi_alpha[ i_code & 0x3 ] == 0x00 ) |
693 | 0 | { |
694 | 0 | i_transparent_code = i_code; |
695 | 0 | } |
696 | 0 | else |
697 | 0 | { |
698 | 0 | p_spu_data->b_auto_crop = false; |
699 | 0 | } |
700 | 0 | } |
701 | |
|
702 | 0 | if( i_code == i_transparent_code ) |
703 | 0 | { |
704 | 0 | if( b_empty_top ) |
705 | 0 | { |
706 | | /* This is a blank top line, we skip it */ |
707 | 0 | i_skipped_top++; |
708 | 0 | } |
709 | 0 | else |
710 | 0 | { |
711 | | /* We can't be sure the current lines will be skipped, |
712 | | * so we store the code just in case. */ |
713 | 0 | *p_dest++ = i_code; |
714 | 0 | i_skipped_bottom++; |
715 | 0 | } |
716 | 0 | } |
717 | 0 | else |
718 | 0 | { |
719 | | /* We got a valid code, store it */ |
720 | 0 | *p_dest++ = i_code; |
721 | | |
722 | | /* Valid code means no blank line */ |
723 | 0 | b_empty_top = false; |
724 | 0 | i_skipped_bottom = 0; |
725 | 0 | } |
726 | 0 | } |
727 | 0 | else |
728 | 0 | { |
729 | 0 | *p_dest++ = i_code; |
730 | 0 | } |
731 | 0 | } |
732 | | |
733 | | /* Check that we didn't go too far */ |
734 | 0 | if( i_x > i_width ) |
735 | 0 | { |
736 | 0 | msg_Err( p_dec, "i_x overflowed, %i > %i", i_x, i_width ); |
737 | 0 | return VLC_EGENERIC; |
738 | 0 | } |
739 | | |
740 | | /* Byte-align the stream */ |
741 | 0 | if( *pi_offset & 0x1 ) |
742 | 0 | { |
743 | 0 | (*pi_offset)++; |
744 | 0 | } |
745 | | |
746 | | /* Swap fields */ |
747 | 0 | i_id = ~i_id & 0x1; |
748 | 0 | } |
749 | | |
750 | | /* We shouldn't get any padding bytes */ |
751 | 1 | if( i_y < i_height ) |
752 | 0 | { |
753 | 0 | msg_Err( p_dec, "padding bytes found in RLE sequence" ); |
754 | 0 | msg_Err( p_dec, "send mail to <sam@zoy.org> if you " |
755 | 0 | "want to help debugging this" ); |
756 | | |
757 | | /* Skip them just in case */ |
758 | 0 | while( i_y < i_height ) |
759 | 0 | { |
760 | 0 | *p_dest++ = i_width << 2; |
761 | 0 | i_y++; |
762 | 0 | } |
763 | |
|
764 | 0 | return VLC_EGENERIC; |
765 | 0 | } |
766 | | |
767 | | #ifdef DEBUG_SPUDEC |
768 | | msg_Dbg( p_dec, "valid subtitle, size: %ix%i, position: %i,%i", |
769 | | p_spu_properties->i_width, p_spu_properties->i_height, |
770 | | p_spu_properties->i_x, p_spu_properties->i_y ); |
771 | | #endif |
772 | | |
773 | | /* Crop if necessary */ |
774 | 1 | if( i_skipped_top || i_skipped_bottom ) |
775 | 0 | { |
776 | 0 | p_spu_data->i_y_top_offset = i_skipped_top; |
777 | 0 | p_spu_data->i_y_bottom_offset = i_skipped_bottom; |
778 | | #ifdef DEBUG_SPUDEC |
779 | | msg_Dbg( p_dec, "cropped to: %ix%i, position: %i,%i", |
780 | | p_spu_properties->i_width, |
781 | | p_spu_properties->i_height - (i_skipped_top + i_skipped_bottom), |
782 | | p_spu_properties->i_x, p_spu_properties->i_y + i_skipped_top ); |
783 | | #endif |
784 | 0 | } |
785 | | |
786 | | /* Handle color if no palette was found */ |
787 | 1 | if( !p_spu_data->b_palette ) |
788 | 1 | { |
789 | 1 | int i, i_inner = -1, i_shade = -1; |
790 | | |
791 | | /* Set the border color */ |
792 | 1 | if( i_border != -1 ) |
793 | 0 | { |
794 | 0 | p_spu_data->pi_yuv[i_border][0] = 0x00; |
795 | 0 | p_spu_data->pi_yuv[i_border][1] = 0x80; |
796 | 0 | p_spu_data->pi_yuv[i_border][2] = 0x80; |
797 | 0 | stats[i_border] = 0; |
798 | 0 | } |
799 | | |
800 | | /* Find the inner colors */ |
801 | 5 | for( i = 0 ; i < 4 && i_inner == -1 ; i++ ) |
802 | 4 | { |
803 | 4 | if( stats[i] ) |
804 | 0 | { |
805 | 0 | i_inner = i; |
806 | 0 | } |
807 | 4 | } |
808 | | |
809 | 1 | for( ; i < 4 && i_shade == -1 ; i++ ) |
810 | 0 | { |
811 | 0 | if( stats[i] ) |
812 | 0 | { |
813 | 0 | if( stats[i] > stats[i_inner] ) |
814 | 0 | { |
815 | 0 | i_shade = i_inner; |
816 | 0 | i_inner = i; |
817 | 0 | } |
818 | 0 | else |
819 | 0 | { |
820 | 0 | i_shade = i; |
821 | 0 | } |
822 | 0 | } |
823 | 0 | } |
824 | | |
825 | | /* Set the inner color */ |
826 | 1 | if( i_inner != -1 ) |
827 | 0 | { |
828 | 0 | p_spu_data->pi_yuv[i_inner][0] = 0xff; |
829 | 0 | p_spu_data->pi_yuv[i_inner][1] = 0x80; |
830 | 0 | p_spu_data->pi_yuv[i_inner][2] = 0x80; |
831 | 0 | } |
832 | | |
833 | | /* Set the anti-aliasing color */ |
834 | 1 | if( i_shade != -1 ) |
835 | 0 | { |
836 | 0 | p_spu_data->pi_yuv[i_shade][0] = 0x80; |
837 | 0 | p_spu_data->pi_yuv[i_shade][1] = 0x80; |
838 | 0 | p_spu_data->pi_yuv[i_shade][2] = 0x80; |
839 | 0 | } |
840 | | |
841 | | #ifdef DEBUG_SPUDEC |
842 | | msg_Dbg( p_dec, "using custom palette (border %i, inner %i, shade %i)", |
843 | | i_border, i_inner, i_shade ); |
844 | | #endif |
845 | 1 | } |
846 | | |
847 | 1 | return VLC_SUCCESS; |
848 | 1 | } |
849 | | |
850 | | static int Render( decoder_t *p_dec, subpicture_t *p_spu, |
851 | | const uint16_t *p_pixeldata, |
852 | | const subpicture_data_t *p_spu_data, |
853 | | const spu_properties_t *p_spu_properties ) |
854 | 1 | { |
855 | 1 | uint8_t *p_p; |
856 | 1 | int i_x, i_y, i_len, i_color, i_pitch; |
857 | 1 | const uint16_t *p_source = p_pixeldata; |
858 | 1 | video_format_t fmt; |
859 | 1 | video_palette_t palette; |
860 | 1 | const int width = p_spu_properties->i_width; |
861 | 1 | const int height = p_spu_properties->i_height - |
862 | 1 | p_spu_data->i_y_top_offset - p_spu_data->i_y_bottom_offset; |
863 | | |
864 | | /* Create a new subpicture region */ |
865 | 1 | video_format_Init( &fmt, VLC_CODEC_YUVP ); |
866 | 1 | video_format_Setup( &fmt, VLC_CODEC_YUVP, width, height, width, height, |
867 | 1 | 0, /* 0 means use aspect ratio of background video */ |
868 | 1 | 1 ); |
869 | 1 | fmt.p_palette = &palette; |
870 | 1 | fmt.p_palette->i_entries = 4; |
871 | 5 | for( i_x = 0; i_x < fmt.p_palette->i_entries; i_x++ ) |
872 | 4 | { |
873 | 4 | fmt.p_palette->palette[i_x][0] = p_spu_data->pi_yuv[i_x][0]; |
874 | 4 | fmt.p_palette->palette[i_x][1] = p_spu_data->pi_yuv[i_x][1]; |
875 | 4 | fmt.p_palette->palette[i_x][2] = p_spu_data->pi_yuv[i_x][2]; |
876 | 4 | fmt.p_palette->palette[i_x][3] = p_spu_data->pi_alpha[i_x] * 0x11; |
877 | 4 | } |
878 | 1 | fmt.color_range = COLOR_RANGE_LIMITED; |
879 | | |
880 | 1 | subpicture_region_t *p_region = subpicture_region_New( &fmt ); |
881 | 1 | fmt.p_palette = NULL; |
882 | 1 | video_format_Clean( &fmt ); |
883 | 1 | if( !p_region ) |
884 | 0 | { |
885 | 0 | msg_Err( p_dec, "cannot allocate SPU region" ); |
886 | 0 | return VLC_EGENERIC; |
887 | 0 | } |
888 | 1 | vlc_spu_regions_push(&p_spu->regions, p_region); |
889 | | |
890 | 1 | p_region->b_absolute = true; p_region->b_in_window = false; |
891 | 1 | p_region->i_x = p_spu_properties->i_x; |
892 | 1 | p_region->i_y = p_spu_properties->i_y + p_spu_data->i_y_top_offset; |
893 | 1 | p_p = p_region->p_picture->p->p_pixels; |
894 | 1 | i_pitch = p_region->p_picture->p->i_pitch; |
895 | | |
896 | | /* Draw until we reach the bottom of the subtitle */ |
897 | 1 | for( i_y = 0; i_y < height * i_pitch; i_y += i_pitch ) |
898 | 0 | { |
899 | | /* Draw until we reach the end of the line */ |
900 | 0 | for( i_x = 0 ; i_x < width; i_x += i_len ) |
901 | 0 | { |
902 | | /* Get the RLE part, then draw the line */ |
903 | 0 | i_color = *p_source & 0x3; |
904 | 0 | i_len = *p_source++ >> 2; |
905 | 0 | memset( p_p + i_x + i_y, i_color, i_len ); |
906 | 0 | } |
907 | 0 | } |
908 | | |
909 | 1 | return VLC_SUCCESS; |
910 | 1 | } |