/src/vlc/modules/codec/cvdsub.c
Line | Count | Source (jump to first uncovered line) |
1 | | /***************************************************************************** |
2 | | * cvdsub.c : CVD Subtitle decoder |
3 | | ***************************************************************************** |
4 | | * Copyright (C) 2003, 2004 VLC authors and VideoLAN |
5 | | * |
6 | | * Authors: Rocky Bernstein |
7 | | * Gildas Bazin <gbazin@videolan.org> |
8 | | * Julio Sanchez Fernandez (http://subhandler.sourceforge.net) |
9 | | * Laurent Aimar <fenrir@via.ecp.fr> |
10 | | * |
11 | | * This program is free software; you can redistribute it and/or modify it |
12 | | * under the terms of the GNU Lesser General Public License as published by |
13 | | * the Free Software Foundation; either version 2.1 of the License, or |
14 | | * (at your option) any later version. |
15 | | * |
16 | | * This program is distributed in the hope that it will be useful, |
17 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
18 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
19 | | * GNU Lesser General Public License for more details. |
20 | | * |
21 | | * You should have received a copy of the GNU Lesser General Public License |
22 | | * along with this program; if not, write to the Free Software Foundation, |
23 | | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
24 | | *****************************************************************************/ |
25 | | |
26 | | /***************************************************************************** |
27 | | * Preamble |
28 | | *****************************************************************************/ |
29 | | #ifdef HAVE_CONFIG_H |
30 | | # include "config.h" |
31 | | #endif |
32 | | |
33 | | #include <vlc_common.h> |
34 | | #include <vlc_plugin.h> |
35 | | #include <vlc_codec.h> |
36 | | |
37 | | #include <vlc_bits.h> |
38 | | |
39 | | #include "../demux/mpeg/timestamps.h" |
40 | | |
41 | | #define DEBUG_CVDSUB 1 |
42 | | |
43 | | /***************************************************************************** |
44 | | * Module descriptor. |
45 | | *****************************************************************************/ |
46 | | static int DecoderOpen ( vlc_object_t * ); |
47 | | static int PacketizerOpen( vlc_object_t * ); |
48 | | static void DecoderClose ( vlc_object_t * ); |
49 | | |
50 | 0 | vlc_module_begin () |
51 | 0 | set_description( N_("CVD subtitle decoder") ) |
52 | 0 | set_capability( "spu decoder", 50 ) |
53 | 0 | set_callbacks( DecoderOpen, DecoderClose ) |
54 | | |
55 | 0 | add_submodule () |
56 | 0 | set_description( N_("Chaoji VCD subtitle packetizer") ) |
57 | 0 | set_capability( "packetizer", 50 ) |
58 | 0 | set_callbacks( PacketizerOpen, DecoderClose ) |
59 | 0 | vlc_module_end () |
60 | | |
61 | | /***************************************************************************** |
62 | | * Local prototypes |
63 | | *****************************************************************************/ |
64 | | static int Decode( decoder_t *, block_t * ); |
65 | | static block_t *Packetize ( decoder_t *, block_t ** ); |
66 | | static block_t *Reassemble ( decoder_t *, block_t * ); |
67 | | static void ParseMetaInfo ( decoder_t *, block_t * ); |
68 | | static void ParseHeader ( decoder_t *, block_t * ); |
69 | | static subpicture_t *DecodePacket( decoder_t *, block_t * ); |
70 | | static void RenderImage( decoder_t *, block_t *, subpicture_region_t * ); |
71 | | |
72 | 0 | #define SUBTITLE_BLOCK_EMPTY 0 |
73 | 0 | #define SUBTITLE_BLOCK_PARTIAL 1 |
74 | | #define SUBTITLE_BLOCK_COMPLETE 2 |
75 | | |
76 | | typedef struct |
77 | | { |
78 | | int b_packetizer; |
79 | | |
80 | | int i_state; /* data-gathering state for this subtitle */ |
81 | | |
82 | | block_t *p_spu; /* Bytes of the packet. */ |
83 | | |
84 | | size_t i_spu_size; /* goal for subtitle_data_pos while gathering, |
85 | | size of used subtitle_data later */ |
86 | | |
87 | | uint16_t i_image_offset; /* offset from subtitle_data to compressed |
88 | | image data */ |
89 | | size_t i_image_length; /* size of the compressed image data */ |
90 | | size_t first_field_offset; /* offset of even raster lines */ |
91 | | size_t second_field_offset; /* offset of odd raster lines */ |
92 | | size_t metadata_offset; /* offset to data describing the image */ |
93 | | size_t metadata_length; /* length of metadata */ |
94 | | |
95 | | vlc_tick_t i_duration; /* how long to display the image, 0 stands |
96 | | for "until next subtitle" */ |
97 | | |
98 | | uint16_t i_x_start, i_y_start; /* position of top leftmost pixel of |
99 | | image when displayed */ |
100 | | uint16_t i_width, i_height; /* dimensions in pixels of image */ |
101 | | |
102 | | uint8_t p_palette[4][4]; /* Palette of colors used in subtitle */ |
103 | | uint8_t p_palette_highlight[4][4]; |
104 | | } decoder_sys_t; |
105 | | |
106 | | static int OpenCommon( vlc_object_t *p_this, bool b_packetizer ) |
107 | 0 | { |
108 | 0 | decoder_t *p_dec = (decoder_t*)p_this; |
109 | 0 | decoder_sys_t *p_sys; |
110 | |
|
111 | 0 | if( p_dec->fmt_in->i_codec != VLC_CODEC_CVD ) |
112 | 0 | return VLC_EGENERIC; |
113 | | |
114 | 0 | p_dec->p_sys = p_sys = malloc( sizeof( decoder_sys_t ) ); |
115 | 0 | if( !p_sys ) |
116 | 0 | return VLC_ENOMEM; |
117 | | |
118 | 0 | p_sys->b_packetizer = b_packetizer; |
119 | |
|
120 | 0 | p_sys->i_state = SUBTITLE_BLOCK_EMPTY; |
121 | 0 | p_sys->p_spu = NULL; |
122 | |
|
123 | 0 | if( b_packetizer ) |
124 | 0 | { |
125 | 0 | p_dec->pf_packetize = Packetize; |
126 | 0 | p_dec->fmt_out.i_codec = VLC_CODEC_CVD; |
127 | 0 | } |
128 | 0 | else |
129 | 0 | { |
130 | 0 | p_dec->pf_decode = Decode; |
131 | 0 | p_dec->fmt_out.i_codec = VLC_CODEC_YUVP; |
132 | 0 | } |
133 | |
|
134 | 0 | return VLC_SUCCESS; |
135 | 0 | } |
136 | | /***************************************************************************** |
137 | | * DecoderOpen: open/initialize the cvdsub decoder. |
138 | | *****************************************************************************/ |
139 | | static int DecoderOpen( vlc_object_t *p_this ) |
140 | 0 | { |
141 | 0 | return OpenCommon( p_this, false ); |
142 | 0 | } |
143 | | |
144 | | /***************************************************************************** |
145 | | * PacketizerOpen: open/initialize the cvdsub packetizer. |
146 | | *****************************************************************************/ |
147 | | static int PacketizerOpen( vlc_object_t *p_this ) |
148 | 0 | { |
149 | 0 | return OpenCommon( p_this, true ); |
150 | 0 | } |
151 | | |
152 | | /***************************************************************************** |
153 | | * DecoderClose: closes the cvdsub decoder/packetizer. |
154 | | *****************************************************************************/ |
155 | | void DecoderClose( vlc_object_t *p_this ) |
156 | 0 | { |
157 | 0 | decoder_t *p_dec = (decoder_t*)p_this; |
158 | 0 | decoder_sys_t *p_sys = p_dec->p_sys; |
159 | |
|
160 | 0 | if( p_sys->p_spu ) block_ChainRelease( p_sys->p_spu ); |
161 | 0 | free( p_sys ); |
162 | 0 | } |
163 | | |
164 | | /***************************************************************************** |
165 | | * Decode: |
166 | | *****************************************************************************/ |
167 | | static int Decode( decoder_t *p_dec, block_t *p_block ) |
168 | 0 | { |
169 | 0 | block_t *p_data; |
170 | |
|
171 | 0 | if( p_block == NULL ) /* No Drain */ |
172 | 0 | return VLCDEC_SUCCESS; |
173 | | |
174 | 0 | if( p_block->i_flags & BLOCK_FLAG_CORRUPTED ) |
175 | 0 | { |
176 | 0 | block_Release( p_block ); |
177 | 0 | return VLCDEC_SUCCESS; |
178 | 0 | } |
179 | | |
180 | 0 | if( !(p_data = Reassemble( p_dec, p_block )) ) |
181 | 0 | return VLCDEC_SUCCESS; |
182 | | |
183 | | /* Parse and decode */ |
184 | 0 | subpicture_t *p_spu = DecodePacket( p_dec, p_data ); |
185 | 0 | block_Release( p_data ); |
186 | 0 | if( p_spu != NULL ) |
187 | 0 | decoder_QueueSub( p_dec, p_spu ); |
188 | 0 | return VLCDEC_SUCCESS; |
189 | 0 | } |
190 | | |
191 | | /***************************************************************************** |
192 | | * Packetize: |
193 | | *****************************************************************************/ |
194 | | static block_t *Packetize( decoder_t *p_dec, block_t **pp_block ) |
195 | 0 | { |
196 | 0 | block_t *p_block, *p_spu; |
197 | |
|
198 | 0 | if( pp_block == NULL || *pp_block == NULL ) return NULL; |
199 | | |
200 | 0 | p_block = *pp_block; |
201 | 0 | *pp_block = NULL; |
202 | |
|
203 | 0 | if( !(p_spu = Reassemble( p_dec, p_block )) ) return NULL; |
204 | | |
205 | 0 | p_spu->i_dts = p_spu->i_pts; |
206 | 0 | p_spu->i_length = VLC_TICK_INVALID; |
207 | |
|
208 | 0 | return p_spu; |
209 | 0 | } |
210 | | |
211 | | |
212 | | /***************************************************************************** |
213 | | Reassemble: |
214 | | |
215 | | Data for single screen subtitle may come in several non-contiguous |
216 | | packets of a stream. This routine is called when the next packet in |
217 | | the stream comes in. The job of this routine is to parse the header, |
218 | | if this is the beginning, and combine the packets into one complete |
219 | | subtitle unit. |
220 | | |
221 | | If everything is complete, we will return a block. Otherwise return |
222 | | NULL. |
223 | | |
224 | | *****************************************************************************/ |
225 | 0 | #define SPU_HEADER_LEN 1 |
226 | | |
227 | | static block_t *Reassemble( decoder_t *p_dec, block_t *p_block ) |
228 | 0 | { |
229 | 0 | decoder_sys_t *p_sys = p_dec->p_sys; |
230 | |
|
231 | 0 | if( p_block->i_buffer < SPU_HEADER_LEN ) |
232 | 0 | { |
233 | 0 | msg_Dbg( p_dec, "invalid packet header (size %zu < %u)" , |
234 | 0 | p_block->i_buffer, SPU_HEADER_LEN ); |
235 | 0 | block_Release( p_block ); |
236 | 0 | return NULL; |
237 | 0 | } |
238 | | |
239 | | /* From the scant data on the format, there is only only way known |
240 | | * to detect the first packet in a subtitle. The first packet |
241 | | * seems to have a valid PTS while later packets for the same |
242 | | * image don't. */ |
243 | 0 | if( p_sys->i_state == SUBTITLE_BLOCK_EMPTY && p_block->i_pts == VLC_TICK_INVALID ) |
244 | 0 | { |
245 | 0 | msg_Warn( p_dec, "first packet expected but no PTS present"); |
246 | 0 | return NULL; |
247 | 0 | } |
248 | | |
249 | 0 | p_block->p_buffer += SPU_HEADER_LEN; |
250 | 0 | p_block->i_buffer -= SPU_HEADER_LEN; |
251 | | |
252 | | /* First packet in the subtitle block */ |
253 | 0 | if( p_sys->i_state == SUBTITLE_BLOCK_EMPTY ) ParseHeader( p_dec, p_block ); |
254 | |
|
255 | 0 | block_ChainAppend( &p_sys->p_spu, p_block ); |
256 | 0 | p_sys->p_spu = block_ChainGather( p_sys->p_spu ); |
257 | |
|
258 | 0 | if( p_sys->p_spu->i_buffer >= p_sys->i_spu_size ) |
259 | 0 | { |
260 | 0 | block_t *p_spu = p_sys->p_spu; |
261 | |
|
262 | 0 | if( p_spu->i_buffer != p_sys->i_spu_size ) |
263 | 0 | { |
264 | 0 | msg_Warn( p_dec, "SPU packets size=%zu should be %zu", |
265 | 0 | p_spu->i_buffer, p_sys->i_spu_size ); |
266 | 0 | } |
267 | |
|
268 | 0 | msg_Dbg( p_dec, "subtitle packet complete, size=%zuu", p_spu->i_buffer); |
269 | |
|
270 | 0 | ParseMetaInfo( p_dec, p_spu ); |
271 | |
|
272 | 0 | p_sys->i_state = SUBTITLE_BLOCK_EMPTY; |
273 | 0 | p_sys->p_spu = 0; |
274 | 0 | return p_spu; |
275 | 0 | } |
276 | 0 | else |
277 | 0 | { |
278 | | /* Not last block in subtitle, so wait for another. */ |
279 | 0 | p_sys->i_state = SUBTITLE_BLOCK_PARTIAL; |
280 | 0 | } |
281 | | |
282 | 0 | return NULL; |
283 | 0 | } |
284 | | |
285 | | /* |
286 | | We do not have information on the subtitle format used on CVD's |
287 | | except the submux sample code and a couple of samples of dubious |
288 | | origin. Thus, this is the result of reading some code whose |
289 | | correctness is not known and some experimentation. |
290 | | |
291 | | CVD subtitles are different in several ways from SVCD OGT subtitles. |
292 | | Image comes first and metadata is at the end. So that the metadata |
293 | | can be found easily, the subtitle packet starts with two bytes |
294 | | (everything is big-endian again) that give the total size of the |
295 | | subtitle data and the offset to the metadata - i.e. size of the |
296 | | image data plus the four bytes at the beginning. |
297 | | |
298 | | Image data comes interlaced is run-length encoded. Each field is a |
299 | | four-bit nibble. Each nibble contains a two-bit repeat count and a |
300 | | two-bit color number so that up to three pixels can be described in |
301 | | four bits. The function of a 0 repeat count is unknown; it might be |
302 | | used for RLE extension. However when the full nibble is zero, the |
303 | | rest of the line is filled with the color value in the next nibble. |
304 | | It is unknown what happens if the color value is greater than three. |
305 | | The rest seems to use a 4-entries palette. It is not impossible |
306 | | that the fill-line complete case above is not as described and the |
307 | | zero repeat count means fill line. The sample code never produces |
308 | | this, so it may be untested. |
309 | | */ |
310 | | |
311 | | static void ParseHeader( decoder_t *p_dec, block_t *p_block ) |
312 | 0 | { |
313 | 0 | decoder_sys_t *p_sys = p_dec->p_sys; |
314 | 0 | uint8_t *p = p_block->p_buffer; |
315 | |
|
316 | 0 | p_sys->i_spu_size = (p[0] << 8) + p[1] + 4; p += 2; |
317 | | |
318 | | /* FIXME: check data sanity */ |
319 | 0 | p_sys->metadata_offset = (p[0] << 8) + p[1]; p +=2; |
320 | 0 | p_sys->metadata_length = p_sys->i_spu_size - p_sys->metadata_offset; |
321 | |
|
322 | 0 | p_sys->i_image_offset = 4; |
323 | 0 | p_sys->i_image_length = p_sys->metadata_offset - p_sys->i_image_offset; |
324 | |
|
325 | 0 | #ifdef DEBUG_CVDSUB |
326 | 0 | msg_Dbg( p_dec, "total size: %zu image size: %zu", |
327 | 0 | p_sys->i_spu_size, p_sys->i_image_length ); |
328 | 0 | #endif |
329 | 0 | } |
330 | | |
331 | | /* |
332 | | We parse the metadata information here. |
333 | | |
334 | | Although metadata information does not have to come in a fixed field |
335 | | order, every metadata field consists of a tag byte followed by |
336 | | parameters. In all cases known, the size including tag byte is |
337 | | exactly four bytes in length. |
338 | | */ |
339 | | |
340 | 0 | #define ExtractXY(x, y) x = ((p[1]&0x0f)<<6) + (p[2]>>2); \ |
341 | 0 | y = ((p[2]&0x03)<<8) + p[3]; |
342 | | |
343 | | static void ParseMetaInfo( decoder_t *p_dec, block_t *p_spu ) |
344 | 0 | { |
345 | | /* Last packet in subtitle block. */ |
346 | |
|
347 | 0 | decoder_sys_t *p_sys = p_dec->p_sys; |
348 | 0 | uint8_t *p = p_spu->p_buffer + p_sys->metadata_offset; |
349 | 0 | uint8_t *p_end = p + p_sys->metadata_length; |
350 | |
|
351 | 0 | for( ; p < p_end; p += 4 ) |
352 | 0 | { |
353 | 0 | switch( p[0] ) |
354 | 0 | { |
355 | 0 | case 0x04: /* subtitle duration in 1/90000ths of a second */ |
356 | 0 | p_sys->i_duration = FROM_SCALE_NZ( (p[1]<<16) + (p[2]<<8) + p[3] ); |
357 | |
|
358 | 0 | #ifdef DEBUG_CVDSUB |
359 | 0 | msg_Dbg( p_dec, "subtitle display duration %"PRIu64" ms", |
360 | 0 | MS_FROM_VLC_TICK(p_sys->i_duration) ); |
361 | 0 | #endif |
362 | 0 | break; |
363 | | |
364 | 0 | case 0x0c: /* unknown */ |
365 | 0 | #ifdef DEBUG_CVDSUB |
366 | 0 | msg_Dbg( p_dec, "subtitle command unknown " |
367 | 0 | "0x%02"PRIx8" 0x%02"PRIx8" 0x%02"PRIx8" 0x%02"PRIx8, |
368 | 0 | p[0], p[1], p[2], p[3] ); |
369 | 0 | #endif |
370 | 0 | break; |
371 | | |
372 | 0 | case 0x17: /* coordinates of subtitle upper left x, y position */ |
373 | 0 | ExtractXY(p_sys->i_x_start, p_sys->i_y_start); |
374 | |
|
375 | 0 | #ifdef DEBUG_CVDSUB |
376 | 0 | msg_Dbg( p_dec, "start position (%"PRIu16",%"PRIu16")", |
377 | 0 | p_sys->i_x_start, p_sys->i_y_start ); |
378 | 0 | #endif |
379 | 0 | break; |
380 | | |
381 | 0 | case 0x1f: /* coordinates of subtitle bottom right x, y position */ |
382 | 0 | { |
383 | 0 | int lastx; |
384 | 0 | int lasty; |
385 | 0 | ExtractXY(lastx, lasty); |
386 | 0 | p_sys->i_width = lastx - p_sys->i_x_start + 1; |
387 | 0 | p_sys->i_height = lasty - p_sys->i_y_start + 1; |
388 | |
|
389 | 0 | #ifdef DEBUG_CVDSUB |
390 | 0 | msg_Dbg( p_dec, "end position (%d,%d), w x h: %"PRIu16"x%"PRIu16, |
391 | 0 | lastx, lasty, p_sys->i_width, p_sys->i_height ); |
392 | 0 | #endif |
393 | 0 | break; |
394 | 0 | } |
395 | | |
396 | 0 | case 0x24: |
397 | 0 | case 0x25: |
398 | 0 | case 0x26: |
399 | 0 | case 0x27: |
400 | 0 | { |
401 | 0 | uint8_t v = p[0] - 0x24; |
402 | |
|
403 | 0 | #ifdef DEBUG_CVDSUB |
404 | | /* Primary Palette */ |
405 | 0 | msg_Dbg( p_dec, "primary palette %"PRIu8" (y,u,v): " |
406 | 0 | "(0x%02"PRIx8",0x%02"PRIx8",0x%02"PRIx8")", |
407 | 0 | v, p[1], p[2], p[3] ); |
408 | 0 | #endif |
409 | |
|
410 | 0 | p_sys->p_palette[v][0] = p[1]; /* Y */ |
411 | 0 | p_sys->p_palette[v][1] = p[3]; /* Cr / V */ |
412 | 0 | p_sys->p_palette[v][2] = p[2]; /* Cb / U */ |
413 | 0 | break; |
414 | 0 | } |
415 | | |
416 | 0 | case 0x2c: |
417 | 0 | case 0x2d: |
418 | 0 | case 0x2e: |
419 | 0 | case 0x2f: |
420 | 0 | { |
421 | 0 | uint8_t v = p[0] - 0x2c; |
422 | |
|
423 | 0 | #ifdef DEBUG_CVDSUB |
424 | 0 | msg_Dbg( p_dec,"highlight palette %"PRIu8" (y,u,v): " |
425 | 0 | "(0x%02"PRIx8",0x%02"PRIx8",0x%02"PRIx8")", |
426 | 0 | v, p[1], p[2], p[3] ); |
427 | 0 | #endif |
428 | | |
429 | | /* Highlight Palette */ |
430 | 0 | p_sys->p_palette_highlight[v][0] = p[1]; /* Y */ |
431 | 0 | p_sys->p_palette_highlight[v][1] = p[3]; /* Cr / V */ |
432 | 0 | p_sys->p_palette_highlight[v][2] = p[2]; /* Cb / U */ |
433 | 0 | break; |
434 | 0 | } |
435 | | |
436 | 0 | case 0x37: |
437 | | /* transparency for primary palette */ |
438 | 0 | p_sys->p_palette[0][3] = (p[3] & 0x0f) << 4; |
439 | 0 | p_sys->p_palette[1][3] = (p[3] >> 4) << 4; |
440 | 0 | p_sys->p_palette[2][3] = (p[2] & 0x0f) << 4; |
441 | 0 | p_sys->p_palette[3][3] = (p[2] >> 4) << 4; |
442 | |
|
443 | 0 | #ifdef DEBUG_CVDSUB |
444 | 0 | msg_Dbg( p_dec, "transparency for primary palette 0..3: " |
445 | 0 | "0x%02"PRIx8" 0x%02"PRIx8" 0x%02"PRIx8" 0x%02"PRIx8, |
446 | 0 | p_sys->p_palette[0][3], p_sys->p_palette[1][3], |
447 | 0 | p_sys->p_palette[2][3], p_sys->p_palette[3][3]); |
448 | 0 | #endif |
449 | 0 | break; |
450 | | |
451 | 0 | case 0x3f: |
452 | | /* transparency for highlight palette */ |
453 | 0 | p_sys->p_palette_highlight[0][3] = (p[2] & 0x0f) << 4; |
454 | 0 | p_sys->p_palette_highlight[1][3] = (p[2] >> 4) << 4; |
455 | 0 | p_sys->p_palette_highlight[2][3] = (p[1] & 0x0f) << 4; |
456 | 0 | p_sys->p_palette_highlight[3][3] = (p[1] >> 4) << 4; |
457 | |
|
458 | 0 | #ifdef DEBUG_CVDSUB |
459 | 0 | msg_Dbg( p_dec, "transparency for highlight palette 0..3: " |
460 | 0 | "0x%02"PRIx8" 0x%02"PRIx8" 0x%02"PRIx8" 0x%02"PRIx8, |
461 | 0 | p_sys->p_palette_highlight[0][3], |
462 | 0 | p_sys->p_palette_highlight[1][3], |
463 | 0 | p_sys->p_palette_highlight[2][3], |
464 | 0 | p_sys->p_palette_highlight[3][3] ); |
465 | 0 | #endif |
466 | 0 | break; |
467 | | |
468 | 0 | case 0x47: |
469 | | /* offset to start of even rows of interlaced image, we correct |
470 | | * to make it relative to i_image_offset (usually 4) */ |
471 | 0 | p_sys->first_field_offset = |
472 | 0 | (p[2] << 8) + p[3] - p_sys->i_image_offset; |
473 | 0 | #ifdef DEBUG_CVDSUB |
474 | 0 | msg_Dbg( p_dec, "1st_field_offset %zu", |
475 | 0 | p_sys->first_field_offset ); |
476 | 0 | #endif |
477 | 0 | break; |
478 | | |
479 | 0 | case 0x4f: |
480 | | /* offset to start of odd rows of interlaced image, we correct |
481 | | * to make it relative to i_image_offset (usually 4) */ |
482 | 0 | p_sys->second_field_offset = |
483 | 0 | (p[2] << 8) + p[3] - p_sys->i_image_offset; |
484 | 0 | #ifdef DEBUG_CVDSUB |
485 | 0 | msg_Dbg( p_dec, "2nd_field_offset %zu", |
486 | 0 | p_sys->second_field_offset); |
487 | 0 | #endif |
488 | 0 | break; |
489 | | |
490 | 0 | default: |
491 | 0 | #ifdef DEBUG_CVDSUB |
492 | 0 | msg_Warn( p_dec, "unknown sequence in control header " |
493 | 0 | "0x%02"PRIx8" 0x%02"PRIx8" 0x%02"PRIx8" 0x%02"PRIx8, |
494 | 0 | p[0], p[1], p[2], p[3]); |
495 | 0 | #endif |
496 | 0 | } |
497 | 0 | } |
498 | 0 | } |
499 | | |
500 | | /***************************************************************************** |
501 | | * DecodePacket: parse and decode an SPU packet |
502 | | ***************************************************************************** |
503 | | * This function parses and decodes an SPU packet and, if valid, returns a |
504 | | * subpicture. |
505 | | *****************************************************************************/ |
506 | | static subpicture_t *DecodePacket( decoder_t *p_dec, block_t *p_data ) |
507 | 0 | { |
508 | 0 | decoder_sys_t *p_sys = p_dec->p_sys; |
509 | 0 | subpicture_t *p_spu; |
510 | 0 | subpicture_region_t *p_region; |
511 | 0 | video_format_t fmt; |
512 | 0 | video_palette_t palette; |
513 | 0 | int i; |
514 | | |
515 | | /* Allocate the subpicture internal data. */ |
516 | 0 | p_spu = decoder_NewSubpicture( p_dec, NULL ); |
517 | 0 | if( !p_spu ) return NULL; |
518 | | |
519 | 0 | p_spu->i_start = p_data->i_pts; |
520 | 0 | p_spu->i_stop = p_data->i_pts + p_sys->i_duration; |
521 | 0 | p_spu->b_ephemer = true; |
522 | | |
523 | | /* Create new SPU region */ |
524 | 0 | video_format_Init( &fmt, VLC_CODEC_YUVP ); |
525 | 0 | fmt.i_sar_num = 1; |
526 | 0 | fmt.i_sar_den = 1; |
527 | 0 | fmt.i_width = fmt.i_visible_width = p_sys->i_width; |
528 | 0 | fmt.i_height = fmt.i_visible_height = p_sys->i_height; |
529 | 0 | fmt.i_x_offset = fmt.i_y_offset = 0; |
530 | 0 | fmt.p_palette = &palette; |
531 | 0 | fmt.p_palette->i_entries = 4; |
532 | 0 | for( i = 0; i < fmt.p_palette->i_entries; i++ ) |
533 | 0 | { |
534 | 0 | fmt.p_palette->palette[i][0] = p_sys->p_palette[i][0]; |
535 | 0 | fmt.p_palette->palette[i][1] = p_sys->p_palette[i][1]; |
536 | 0 | fmt.p_palette->palette[i][2] = p_sys->p_palette[i][2]; |
537 | 0 | fmt.p_palette->palette[i][3] = p_sys->p_palette[i][3]; |
538 | 0 | } |
539 | |
|
540 | 0 | p_region = subpicture_region_New( &fmt ); |
541 | 0 | if( !p_region ) |
542 | 0 | { |
543 | 0 | msg_Err( p_dec, "cannot allocate SPU region" ); |
544 | 0 | subpicture_Delete( p_spu ); |
545 | 0 | return NULL; |
546 | 0 | } |
547 | | |
548 | 0 | p_spu->p_region = p_region; |
549 | 0 | p_region->i_x = p_sys->i_x_start; |
550 | 0 | p_region->i_x = p_region->i_x * 3 / 4; /* FIXME: use aspect ratio for x? */ |
551 | 0 | p_region->i_y = p_sys->i_y_start; |
552 | |
|
553 | 0 | RenderImage( p_dec, p_data, p_region ); |
554 | |
|
555 | 0 | return p_spu; |
556 | 0 | } |
557 | | |
558 | | /***************************************************************************** |
559 | | * ParseImage: parse and render the image part of the subtitle |
560 | | ***************************************************************************** |
561 | | This part parses the subtitle graphical data and renders it. |
562 | | |
563 | | Image data comes interlaced and is run-length encoded (RLE). Each |
564 | | field is a four-bit nibbles that is further subdivided in a two-bit |
565 | | repeat count and a two-bit color number - up to three pixels can be |
566 | | described in four bits. What a 0 repeat count means is unknown. It |
567 | | might be used for RLE extension. There is a special case of a 0 |
568 | | repeat count though. When the full nibble is zero, the rest of the |
569 | | line is filled with the color value in the next nibble. It is |
570 | | unknown what happens if the color value is greater than three. The |
571 | | rest seems to use a 4-entries palette. It is not impossible that the |
572 | | fill-line complete case above is not as described and the zero repeat |
573 | | count means fill line. The sample code never produces this, so it |
574 | | may be untested. |
575 | | |
576 | | However we'll transform this so that that the RLE is expanded and |
577 | | interlacing will also be removed. On output each pixel entry will by |
578 | | a 4-bit alpha (filling 8 bits), and 8-bit y, u, and v entry. |
579 | | |
580 | | *****************************************************************************/ |
581 | | static void RenderImage( decoder_t *p_dec, block_t *p_data, |
582 | | subpicture_region_t *p_region ) |
583 | 0 | { |
584 | 0 | decoder_sys_t *p_sys = p_dec->p_sys; |
585 | 0 | uint8_t *p_dest = p_region->p_picture->Y_PIXELS; |
586 | 0 | int i_field; /* The subtitles are interlaced */ |
587 | 0 | int i_row, i_column; /* scanline row/column number */ |
588 | 0 | uint8_t i_color, i_count; |
589 | 0 | bs_t bs; |
590 | |
|
591 | 0 | bs_init( &bs, p_data->p_buffer + p_sys->i_image_offset, |
592 | 0 | p_data->i_buffer - p_sys->i_image_offset ); |
593 | |
|
594 | 0 | for( i_field = 0; i_field < 2; i_field++ ) |
595 | 0 | { |
596 | 0 | for( i_row = i_field; i_row < p_sys->i_height; i_row += 2 ) |
597 | 0 | { |
598 | 0 | for( i_column = 0; i_column < p_sys->i_width; i_column++ ) |
599 | 0 | { |
600 | 0 | uint8_t i_val = bs_read( &bs, 4 ); |
601 | |
|
602 | 0 | if( i_val == 0 ) |
603 | 0 | { |
604 | | /* Fill the rest of the line with next color */ |
605 | 0 | i_color = bs_read( &bs, 4 ); |
606 | |
|
607 | 0 | memset( &p_dest[i_row * p_region->p_picture->Y_PITCH + |
608 | 0 | i_column], i_color, |
609 | 0 | p_sys->i_width - i_column ); |
610 | 0 | i_column = p_sys->i_width; |
611 | 0 | continue; |
612 | 0 | } |
613 | 0 | else |
614 | 0 | { |
615 | | /* Normal case: get color and repeat count */ |
616 | 0 | i_count = (i_val >> 2); |
617 | 0 | i_color = i_val & 0x3; |
618 | |
|
619 | 0 | i_count = __MIN( i_count, p_sys->i_width - i_column ); |
620 | |
|
621 | 0 | memset( &p_dest[i_row * p_region->p_picture->Y_PITCH + |
622 | 0 | i_column], i_color, i_count ); |
623 | 0 | i_column += i_count - 1; |
624 | 0 | continue; |
625 | 0 | } |
626 | 0 | } |
627 | |
|
628 | 0 | bs_align( &bs ); |
629 | 0 | } |
630 | 0 | } |
631 | 0 | } |