]> git.sesse.net Git - vlc/blob - modules/codec/ogt/ogt.c
* ALL: separation of the SPU engine from the VOUT.
[vlc] / modules / codec / ogt / ogt.c
1 /*****************************************************************************
2  * ogt.c : Overlay Graphics Text (SVCD subtitles) decoder thread
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  *       Samuel 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 "ogt.h"
37 #include "common.h"
38
39 /*****************************************************************************
40  * Module descriptor.
41  *****************************************************************************/
42 static int  VCDSubOpen   ( vlc_object_t * );
43 static int  PacketizerOpen( vlc_object_t * );
44
45 vlc_module_begin();
46     set_description( _("Philips OGT (SVCD subtitle) decoder") );
47     set_capability( "decoder", 50 );
48     set_callbacks( VCDSubOpen, VCDSubClose );
49
50     add_integer ( MODULE_STRING "-debug", 0, NULL,
51                   DEBUG_TEXT, DEBUG_LONGTEXT, VLC_TRUE );
52
53     add_integer ( MODULE_STRING "-horizontal-correct", 0, NULL,
54                   HORIZONTAL_CORRECT, HORIZONTAL_CORRECT_LONGTEXT, VLC_FALSE );
55
56     add_integer ( MODULE_STRING "-vertical-correct", 0, NULL,
57                   VERTICAL_CORRECT, VERTICAL_CORRECT_LONGTEXT, VLC_FALSE );
58
59     add_string( MODULE_STRING "-aspect-ratio", "", NULL,
60                 SUB_ASPECT_RATIO_TEXT, SUB_ASPECT_RATIO_LONGTEXT, VLC_TRUE );
61
62     add_integer( MODULE_STRING "-duration-scaling", 9, NULL,
63                  DURATION_SCALE_TEXT, DURATION_SCALE_LONGTEXT, VLC_TRUE );
64
65     add_submodule();
66     set_description( _("Philips OGT (SVCD subtitle) packetizer") );
67     set_capability( "packetizer", 50 );
68     set_callbacks( PacketizerOpen, VCDSubClose );
69 vlc_module_end();
70
71 /*****************************************************************************
72  * Local prototypes
73  *****************************************************************************/
74
75 static block_t *Reassemble ( decoder_t *, block_t ** );
76 static subpicture_t *Decode( decoder_t *, block_t ** );
77 static block_t *Packetize  ( decoder_t *, block_t ** );
78
79 /*****************************************************************************
80  * VCDSubOpen
81  *****************************************************************************
82  * Tries to launch a decoder and return score so that the interface is able
83  * to chose.
84  *****************************************************************************/
85 static int
86 VCDSubOpen( vlc_object_t *p_this )
87 {
88     decoder_t     *p_dec = (decoder_t*)p_this;
89     decoder_sys_t *p_sys;
90
91     if( p_dec->fmt_in.i_codec != VLC_FOURCC( 'o','g','t',' ' ) )
92     {
93         return VLC_EGENERIC;
94     }
95
96
97     p_dec->p_sys = p_sys = malloc( sizeof( decoder_sys_t ) );
98
99     p_sys->i_debug       = config_GetInt( p_this, MODULE_STRING "-debug" );
100     p_sys->b_packetizer  = VLC_FALSE;
101     p_sys->p_vout        = NULL;
102     p_sys->i_image       = -1;
103     p_sys->subtitle_data = NULL;
104
105     VCDSubInitSubtitleBlock( p_sys );
106
107     es_format_Init( &p_dec->fmt_out, SPU_ES, VLC_FOURCC( 'o','g','t',' ' ) );
108
109
110     p_dec->pf_decode_sub = Decode;
111     p_dec->pf_packetize  = Packetize;
112
113     dbg_print( (DECODE_DBG_CALL|DECODE_DBG_EXT) , "");
114
115     return VLC_SUCCESS;
116 }
117
118 /*****************************************************************************
119  * PacketizerOpen
120  *****************************************************************************
121  * Tries to launch a decoder and return score so that the interface is able
122  * to chose.
123  *****************************************************************************/
124 static int PacketizerOpen( vlc_object_t *p_this )
125 {
126     decoder_t *p_dec = (decoder_t*)p_this;
127
128     if( VCDSubOpen( p_this ) )
129     {
130         return VLC_EGENERIC;
131     }
132     p_dec->p_sys->b_packetizer = VLC_TRUE;
133
134     return VLC_SUCCESS;
135 }
136
137 /*****************************************************************************
138  * Decode:
139  *****************************************************************************/
140 static subpicture_t *
141 Decode ( decoder_t *p_dec, block_t **pp_block )
142 {
143     decoder_sys_t *p_sys = p_dec->p_sys;
144     block_t       *p_spu = Reassemble( p_dec, pp_block );
145     vout_thread_t *p_last_vout = p_dec->p_sys->p_vout;
146
147     dbg_print( (DECODE_DBG_CALL) , "");
148
149     if( p_spu )
150     {
151         p_sys->i_spu = block_ChainExtract( p_spu, p_sys->buffer, 65536 );
152         p_sys->i_pts = p_spu->i_pts;
153         block_ChainRelease( p_spu );
154
155         if( ( p_sys->p_vout = VCDSubFindVout( p_dec ) ) )
156         {
157             if( p_last_vout != p_sys->p_vout )
158             {
159                 spu_Control( p_sys->p_vout->p_spu, SPU_CHANNEL_REGISTER,
160                              &p_sys->i_subpic_channel );
161             }
162
163             /* Parse and decode */
164             E_(ParsePacket)( p_dec );
165
166             vlc_object_release( p_sys->p_vout );
167         }
168
169         VCDSubInitSubtitleBlock ( p_sys );
170     }
171
172     return NULL;
173 }
174
175 /*****************************************************************************
176  * Packetize:
177  *****************************************************************************/
178 static block_t *
179 Packetize( decoder_t *p_dec, block_t **pp_block )
180 {
181     decoder_sys_t *p_sys = p_dec->p_sys;
182     block_t       *p_spu = Reassemble( p_dec, pp_block );
183
184     if( p_spu )
185     {
186         p_spu->i_dts = p_spu->i_pts;
187         p_spu->i_length = 0;
188
189         VCDSubInitSubtitleBlock( p_sys );
190
191         return block_ChainGather( p_spu );
192     }
193     return NULL;
194 }
195
196 #define SPU_HEADER_LEN 5
197
198 /*****************************************************************************
199  Reassemble:
200
201  The data for single screen subtitle may come in one of many
202  non-contiguous packets of a stream. This routine is called when the
203  next packet in the stream comes in. The job of this routine is to
204  parse the header, if this is the beginning, and combine the packets
205  into one complete subtitle unit.
206
207  If everything is complete, we will return a block. Otherwise return
208  NULL.
209
210
211  The format of the beginning of the subtitle packet that is used here.
212
213    size    description
214    -------------------------------------------
215    byte    subtitle channel (0..7) in bits 0-3
216    byte    subtitle packet number of this subtitle image 0-N,
217            if the subtitle packet is complete, the top bit of the byte is 1.
218    uint16  subtitle image number
219
220  *****************************************************************************/
221 static block_t *
222 Reassemble( decoder_t *p_dec, block_t **pp_block )
223 {
224     decoder_sys_t *p_sys = p_dec->p_sys;
225     block_t *p_block;
226     uint8_t *p_buffer;
227     uint16_t i_expected_image;
228     uint8_t  i_packet, i_expected_packet;
229
230     if( pp_block == NULL || *pp_block == NULL )
231     {
232         return NULL;
233     }
234     p_block = *pp_block;
235     *pp_block = NULL;
236
237     if( p_block->i_buffer < SPU_HEADER_LEN )
238     {
239       msg_Dbg( p_dec, "invalid packet header (size %d < %d)" ,
240                p_block->i_buffer, SPU_HEADER_LEN );
241       block_Release( p_block );
242       return NULL;
243     }
244
245     p_buffer = p_block->p_buffer;
246
247     dbg_print( (DECODE_DBG_CALL|DECODE_DBG_PACKET),
248                "header: 0x%02x 0x%02x 0x%02x 0x%02x, size: %i",
249                p_buffer[1], p_buffer[2], p_buffer[3], p_buffer[4],
250                p_block->i_buffer);
251
252     /* Attach to our input thread and see if subtitle is selected. */
253     {
254         vlc_object_t * p_input;
255         vlc_value_t val;
256
257         p_input = vlc_object_find( p_dec, VLC_OBJECT_INPUT, FIND_PARENT );
258
259         if( !p_input ) return NULL;
260
261         if( var_Get( p_input, "spu-channel", &val ) )
262         {
263             vlc_object_release( p_input );
264           return NULL;
265         }
266
267         vlc_object_release( p_input );
268         dbg_print( (DECODE_DBG_PACKET),
269                    "val.i_int %x p_buffer[i] %x", val.i_int, p_buffer[1]);
270
271         /* The dummy ES that the menu selection uses has an 0x70 at
272            the head which we need to strip off. */
273         if( val.i_int == -1 || (val.i_int & 0x03) != p_buffer[1] )
274         {
275             dbg_print( DECODE_DBG_PACKET, "subtitle not for us.\n");
276             return NULL;
277         }
278     }
279
280     if ( p_sys->state == SUBTITLE_BLOCK_EMPTY ) {
281       i_expected_image  = p_sys->i_image+1;
282       i_expected_packet = 0;
283     } else {
284       i_expected_image  = p_sys->i_image;
285       i_expected_packet = p_sys->i_packet+1;
286     }
287
288     p_buffer += 2;
289
290     if ( *p_buffer & 0x80 ) {
291       p_sys->state = SUBTITLE_BLOCK_COMPLETE;
292       i_packet     = ( *p_buffer++ & 0x7F );
293     } else {
294       p_sys->state = SUBTITLE_BLOCK_PARTIAL;
295       i_packet     = *p_buffer++;
296     }
297
298     p_sys->i_image = GETINT16(p_buffer);
299
300     if ( p_sys->i_image != i_expected_image ) {
301       msg_Warn( p_dec, "expecting subtitle image %u but found %u",
302                 i_expected_image, p_sys->i_image );
303     }
304
305     if ( i_packet != i_expected_packet ) {
306       msg_Warn( p_dec, "expecting subtitle image packet %u but found %u",
307                 i_expected_packet, i_packet);
308     }
309
310     p_sys->i_packet = i_packet;
311
312     if ( p_sys->i_packet == 0 ) {
313       /* First packet in the subtitle block */
314       E_(ParseHeader)( p_dec, p_buffer, p_block );
315       VCDSubInitSubtitleData(p_sys);
316     }
317
318     /* FIXME - remove append_data and use chainappend */
319     VCDSubAppendData( p_dec, p_buffer, p_block->i_buffer - SPU_HEADER_LEN );
320
321     block_ChainAppend( &p_sys->p_block, p_block );
322
323     p_sys->i_spu += p_block->i_buffer - SPU_HEADER_LEN;
324
325     if (p_sys->state == SUBTITLE_BLOCK_COMPLETE)
326     {
327       if( p_sys->i_spu != p_sys->i_spu_size )
328         {
329           msg_Warn( p_dec, "SPU packets size=%d should be %d",
330                    p_sys->i_spu, p_sys->i_spu_size );
331         }
332
333       dbg_print( (DECODE_DBG_PACKET),
334                  "subtitle packet complete, size=%d", p_sys->i_spu );
335
336       return p_sys->p_block;
337     }
338     return NULL;
339 }
340
341 \f
342 /*
343  * Local variables:
344  *  c-file-style: "gnu"
345  *  tab-width: 8
346  *  indent-tabs-mode: nil
347  * End:
348  */