* ALL: separation of the SPU engine from the VOUT.
[vlc.git] / modules / codec / ogt / ogt_parse.c
1 /*****************************************************************************
2  * Philips OGT (SVCD subtitle) packet parser
3  *****************************************************************************
4  * Copyright (C) 2003, 2004 VideoLAN
5  * $Id$
6  *
7  * Author: Rocky Bernstein 
8  *   based on code from: 
9  *       Julio Sanchez Fernandez (http://subhandler.sourceforge.net)
10  *       Sam Hocevar <sam@zoy.org>
11  *       Laurent Aimar <fenrir@via.ecp.fr>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
26  *****************************************************************************/
27
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31 #include <vlc/vlc.h>
32 #include <vlc/vout.h>
33 #include <vlc/decoder.h>
34
35 #include "subtitle.h"
36 #include "common.h"
37 #include "render.h"
38 #include "ogt.h"
39
40 #ifdef HAVE_LIBPNG
41 #include <png.h>
42 #endif
43
44 /* An image color is a two-bit palette entry: 0..3 */ 
45 typedef uint8_t ogt_color_t;
46
47 /*****************************************************************************
48  * Local prototypes.
49  *****************************************************************************/
50 static int  ParseImage         ( decoder_t *, subpicture_t * );
51
52 /*
53   The format is roughly as follows (everything is big-endian):
54  
55    size     description
56    -------------------------------------------
57    byte     subtitle channel (0..7) in bits 0-3 
58    byte     subtitle packet number of this subtitle image 0-N,
59             if the subtitle packet is complete, the top bit of the byte is 1.
60    u_int16  subtitle image number
61    u_int16  length in bytes of the rest
62    byte     option flags, unknown meaning except bit 3 (0x08) indicates
63             presence of the duration field
64    byte     unknown 
65    u_int32  duration in 1/90000ths of a second (optional), start time
66             is as indicated by the PTS in the PES header
67    u_int32  xpos
68    u_int32  ypos
69    u_int32  width (must be even)
70    u_int32  height (must be even)
71    byte[16] palette, 4 palette entries, each contains values for
72             Y, U, V and transparency, 0 standing for transparent
73    byte     command,
74             cmd>>6==1 indicates shift
75             (cmd>>4)&3 is direction from, (0=top,1=left,2=right,3=bottom)
76    u_int32  shift duration in 1/90000ths of a second
77    u_int16  offset of odd-numbered scanlines - subtitle images are 
78             given in interlace order
79    byte[]   limited RLE image data in interlace order (0,2,4... 1,3,5) with
80             2-bits per palette number
81 */
82
83 /* FIXME: do we really need p_buffer and p? 
84    Can't all of thes _offset's and _lengths's get removed? 
85 */
86 void E_(ParseHeader)( decoder_t *p_dec, uint8_t *p_buffer, block_t *p_block )
87 {
88   decoder_sys_t *p_sys = p_dec->p_sys;
89   uint8_t *p = p_buffer;
90   int i;
91
92   dbg_print( (DECODE_DBG_CALL|DECODE_DBG_EXT) , "");
93
94   p_sys->i_pts    = p_block->i_pts;
95   p_sys->i_spu_size = GETINT16(p);
96   p_sys->i_options  = *p++;
97   p_sys->i_options2 = *p++;
98   
99   if ( p_sys->i_options & 0x08 ) {
100     p_sys->i_duration = GETINT32(p);
101     p_sys->i_duration *= config_GetInt( p_dec, MODULE_STRING 
102                                         "-duration-scaling" );
103   } else {
104     /* 0 means display until next subtitle comes in. */
105     p_sys->i_duration = 0;
106   }
107   p_sys->i_x_start= GETINT16(p);
108   p_sys->i_y_start= GETINT16(p);
109   p_sys->i_width  = GETINT16(p);
110   p_sys->i_height = GETINT16(p);
111   
112   for (i=0; i<4; i++) {
113     p_sys->p_palette[i].s.y = *p++;
114     p_sys->p_palette[i].s.u = *p++;
115     p_sys->p_palette[i].s.v = *p++;
116     /* OGT has 8-bit resolution for alpha, but DVD's and CVDS use 4-bits.
117        Since we want to use the same render routine, rather than scale up
118        CVD (and DVD) subtitles, we'll scale down ours. 
119     */
120     p_sys->p_palette[i].s.t = (*p++) >> 4;
121   }
122   p_sys->i_cmd = *p++;
123       /* We do not really know this, FIXME */
124   if ( p_sys->i_cmd ) {
125     p_sys->i_cmd_arg = GETINT32(p);
126   }
127
128   /* Actually, this is measured against a different origin, so we have to
129      adjust it */
130   p_sys->second_field_offset = GETINT16(p);
131   p_sys->i_image_offset  = p - p_buffer;
132   p_sys->i_image_length  = p_sys->i_spu_size - p_sys->i_image_offset;
133   p_sys->metadata_length = p_sys->i_image_offset;
134
135   if (p_sys && p_sys->i_debug & DECODE_DBG_PACKET) {
136     msg_Dbg( p_dec, "x-start: %d, y-start: %d, width: %d, height %d, "
137              "spu size: %d, duration: %lu (d:%d p:%d)",
138              p_sys->i_x_start, p_sys->i_y_start, 
139              p_sys->i_width, p_sys->i_height, 
140              p_sys->i_spu_size, (long unsigned int) p_sys->i_duration,
141              p_sys->i_image_length, p_sys->i_image_offset);
142     
143     for (i=0; i<4; i++) {
144       msg_Dbg( p_dec, "palette[%d]= T: %2x, Y: %2x, u: %2x, v: %2x", i,
145                p_sys->p_palette[i].s.t, p_sys->p_palette[i].s.y, 
146                p_sys->p_palette[i].s.u, p_sys->p_palette[i].s.v );
147     }
148   }
149 }
150
151
152 /*****************************************************************************
153  * ParsePacket: parse an SPU packet and send it to the video output
154  *****************************************************************************
155  * This function parses the SPU packet and, if valid, sends it to the
156  * video output.
157  *****************************************************************************/
158 void 
159 E_(ParsePacket)( decoder_t *p_dec)
160 {
161     decoder_sys_t *p_sys = p_dec->p_sys;
162
163     subpicture_t  *p_spu;
164
165     dbg_print( (DECODE_DBG_CALL|DECODE_DBG_EXT) , "");
166     
167     /* Allocate the subpicture internal data. */
168     p_spu = spu_CreateSubpicture( p_sys->p_vout->p_spu );
169     if( p_spu == NULL ) return;
170
171     p_spu->i_channel = p_sys->i_subpic_channel;
172
173     /* In ParseImage we expand the run-length encoded color 0's; also
174        we expand pixels and remove the color palette. This should
175        facilitate scaling and antialiasing and speed up rendering.
176     */
177     p_spu->p_sys = malloc( sizeof( subpicture_sys_t ) 
178                            + PIXEL_SIZE * (p_sys->i_width * p_sys->i_height) );
179
180     /* Fill the p_spu structure */
181     vlc_mutex_init( p_dec, &p_spu->p_sys->lock );
182
183     p_spu->pf_render  = VCDSubBlend;
184     p_spu->pf_destroy = VCDSubDestroySPU;
185     p_spu->p_sys->p_data = (uint8_t*)p_spu->p_sys + sizeof( subpicture_sys_t );
186
187     p_spu->p_sys->i_x_end        = p_sys->i_x_start + p_sys->i_width - 1;
188     p_spu->p_sys->i_y_end        = p_sys->i_y_start + p_sys->i_height - 1;
189
190     p_spu->i_x        = p_sys->i_x_start 
191       + config_GetInt( p_dec, MODULE_STRING "-horizontal-correct" );
192
193     p_spu->p_sys->p_palette[0] = p_sys->p_palette[0];
194     p_spu->p_sys->p_palette[1] = p_sys->p_palette[1];
195     p_spu->p_sys->p_palette[2] = p_sys->p_palette[2];
196     p_spu->p_sys->p_palette[3] = p_sys->p_palette[3];
197
198     /* FIXME: use aspect ratio for x? */
199     p_spu->i_x        = (p_spu->i_x * 3) / 4; 
200     p_spu->i_y        = p_sys->i_y_start 
201       + config_GetInt( p_dec, MODULE_STRING "-vertical-correct" );
202     p_spu->i_width    = p_sys->i_width;
203     p_spu->i_height   = p_sys->i_height;
204
205     p_spu->i_start    = p_sys->i_pts;
206     p_spu->i_stop     = p_sys->i_pts + p_sys->i_duration;
207     
208     p_spu->p_sys->b_crop  = VLC_FALSE;
209     p_spu->p_sys->i_debug = p_sys->i_debug;
210
211     /* Get display time now. If we do it later, we may miss the PTS. */
212     p_spu->p_sys->i_pts = p_sys->i_pts;
213
214     /* Attach to our input thread */
215     p_spu->p_sys->p_input = vlc_object_find( p_dec,
216                                              VLC_OBJECT_INPUT, FIND_PARENT );
217
218     /* We try to display it */
219     if( ParseImage( p_dec, p_spu ) )
220     {
221         /* There was a parse error, delete the subpicture */
222         spu_DestroySubpicture( p_sys->p_vout->p_spu, p_spu );
223         return;
224     }
225
226     /* SPU is finished - we can ask the video output to display it */
227     spu_DisplaySubpicture( p_sys->p_vout->p_spu, p_spu );
228
229 }
230
231 /* Advance pointer to image pointer, update internal i_2bit_field counter
232    and check that we haven't goine too far  in the image data. */
233 #define advance_color_pointer_byte                                      \
234   p++;                                                                  \
235   i_2bit_field=4;                                                       \
236   if (p >= maxp) {                                                      \
237     msg_Warn( p_dec,                                                    \
238               "broken subtitle - tried to access beyond end "           \
239               "in image extraction");                                   \
240     return VLC_EGENERIC;                                                \
241   }                                                                     \
242
243 #define advance_color_pointer                                           \
244   i_2bit_field--;                                                       \
245   if ( i_2bit_field == 0 ) {                                            \
246     advance_color_pointer_byte;                                         \
247   }                                                                     
248
249 #define OGT_FIELD_BITS (2)
250 #define OGT_FIELD_MASK  ((1<<OGT_FIELD_BITS) - 1) 
251
252 /* Get the next field - either a palette index or a RLE count for
253    color 0.  To do this we use byte image pointer p, and i_2bit_field
254    which indicates where we are in the byte.
255 */
256 static inline ogt_color_t 
257 ExtractField(uint8_t *p, unsigned int i_2bit_field) 
258 {
259   return ( ( *p >> (OGT_FIELD_BITS*(i_2bit_field-1)) ) & OGT_FIELD_MASK );
260 }
261
262 /*****************************************************************************
263  * ParseImage: parse the image part of the subtitle
264  *****************************************************************************
265  This part parses the subtitle graphical data and stores it in a more
266  convenient structure for later rendering. 
267
268  The image is encoded using two bits per pixel that select a palette
269  entry except that value 0 starts a limited run-length encoding for
270  color 0.  When 0 is seen, the next two bits encode one less than the
271  number of pixels, so we can encode run lengths from 1 to 4. These get
272  filled with the color in palette entry 0.
273
274  The encoding of each line is padded to a whole number of bytes.  The
275  first field is padded to an even byte length and the complete subtitle
276  is padded to a 4-byte multiple that always include one zero byte at
277  the end.
278
279  However we'll transform this so that that the RLE is expanded and
280  interlacing will also be removed. On output each pixel entry will by
281  an 4-bit alpha (filling 8 bits), and 8-bit y, u, and v entry.
282
283  *****************************************************************************/
284 static int 
285 ParseImage( decoder_t *p_dec, subpicture_t * p_spu )
286 {
287     decoder_sys_t *p_sys = p_dec->p_sys;
288
289     unsigned int i_field;  /* The subtitles are interlaced, are we on an
290                               even or odd scanline?  */
291
292     unsigned int i_row;    /* scanline row number */
293     unsigned int i_column; /* scanline column number */
294
295     unsigned int i_width  = p_sys->i_width;
296     unsigned int i_height = p_sys->i_height;
297
298     uint8_t *p_dest = (uint8_t *)p_spu->p_sys->p_data;
299
300     uint8_t i_2bit_field;           /* The 2-bit field to sue in byte of *p.
301                                        Has value 0..4. */
302     uint8_t i_pending_zero = 0;    /* number of pixels to fill with 
303                                       color zero 0..3 */
304     ogt_color_t i_color;           /* current pixel color: 0..3 */
305     uint8_t *p = p_sys->subtitle_data  + p_sys->i_image_offset;
306     uint8_t *maxp = p + p_sys->i_image_length;
307
308     dbg_print( (DECODE_DBG_CALL) , "width x height: %dx%d ",
309                i_width, i_height);
310
311     if (p_sys && p_sys->i_debug & DECODE_DBG_IMAGE)     
312       printf("\n");
313
314     for ( i_field=0; i_field < 2; i_field++ ) {
315       i_2bit_field = 4;
316       for ( i_row=i_field; i_row < i_height; i_row += 2 ) {
317         for ( i_column=0; i_column<i_width; i_column++ ) {
318
319           if ( i_pending_zero ) {
320             /* We are in the middle of a RLE expansion, just decrement and 
321                fall through with current color value */
322             i_pending_zero--;
323             i_color = 0;
324           } else {
325             i_color = ExtractField( p, i_2bit_field );
326             advance_color_pointer;
327             if ( i_color == 0 ) {
328               i_pending_zero = ExtractField( p, i_2bit_field );
329               advance_color_pointer;
330               /* Fall through with i_color == 0 to output the first cell */
331             }
332           }
333
334           /* Color is 0-3. */
335           p_dest[i_row*i_width+i_column] = i_color;
336
337           if (p_sys && p_sys->i_debug & DECODE_DBG_IMAGE)       
338             printf("%1d", i_color);
339
340         }
341
342         if (p_sys && (p_sys->i_debug & DECODE_DBG_IMAGE))
343           printf("\n");
344
345         if ( i_2bit_field != 4 ) {
346           /* Lines are padded to complete bytes, ignore padding */
347           advance_color_pointer_byte;
348         }
349       }
350       p = p_sys->subtitle_data + p_sys->i_image_offset 
351         + p_sys->second_field_offset;
352     }
353
354     if (p_sys && (p_sys->i_debug & DECODE_DBG_IMAGE)) {
355       /* Dump out image not interlaced... */
356       VCDSubDumpImage( p_dest, i_height, i_width );
357     }
358
359 #ifdef HAVE_LIBPNG
360     if (p_sys && (p_sys->i_debug & DECODE_DBG_PNG)) {
361 #define TEXT_COUNT 2
362       /* Dump image to a file in PNG format. */
363       char filename[300];
364       png_text text_ptr[TEXT_COUNT];
365
366       text_ptr[0].key = "Preparer";
367       text_ptr[0].text = "VLC";
368       text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE;
369       text_ptr[1].key = "Description";
370       text_ptr[1].text = "SVCD Subtitle";
371       text_ptr[1].compression = PNG_TEXT_COMPRESSION_NONE;
372
373       snprintf(filename, 300, "%s%d.png", "/tmp/vlc-svcd-sub", p_sys->i_image);
374       VCDSubDumpPNG( p_dest, p_dec, i_height, i_width, filename,
375                      text_ptr, TEXT_COUNT );
376     }
377 #endif /*HAVE_LIBPNG*/
378     
379     VCDSubHandleScaling( p_spu, p_dec );
380     return VLC_SUCCESS;
381 }
382
383 \f
384 /* 
385  * Local variables:
386  *  c-file-style: "gnu"
387  *  tab-width: 8
388  *  indent-tabs-mode: nil
389  * End:
390  */