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