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