]> git.sesse.net Git - vlc/blob - modules/codec/ogt/ogt.c
Keep in synch with rest of source. Should also have no effect on upcoming release
[vlc] / modules / codec / ogt / ogt.c
1 /*****************************************************************************
2  * ogt.c : Overlay Graphics Text (SVCD subtitles) decoder thread
3  *****************************************************************************
4  * Copyright (C) 2003 VideoLAN
5  * $Id: ogt.c,v 1.4 2003/12/26 02:47:59 rocky Exp $
6  *
7  * Authors: 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 "ogt.h"
36
37 #define DEBUG_LONGTEXT N_( \
38     "This integer when viewed in binary is a debugging mask\n" \
39     "external call     1\n" \
40     "all calls         2\n" \
41     "misc              4\n" )
42
43
44 /*****************************************************************************
45  * Module descriptor.
46  *****************************************************************************/
47 static int  DecoderOpen   ( vlc_object_t * );
48 static int  PacketizerOpen( vlc_object_t * );
49
50 static void Close  ( vlc_object_t * );
51
52 vlc_module_begin();
53     set_description( _("Philips OGT (SVCD subtitle) decoder") );
54     set_capability( "decoder", 50 );
55     set_callbacks( DecoderOpen, Close );
56
57     add_integer ( MODULE_STRING "-debug", 0, NULL,
58                   N_("set debug mask for additional debugging."),
59                   N_(DEBUG_LONGTEXT), VLC_TRUE );
60
61     add_submodule();
62     set_description( _("Philips OGT (SVCD subtitle) packetizer") );
63     set_capability( "packetizer", 50 );
64     set_callbacks( PacketizerOpen, Close );
65 vlc_module_end();
66
67 /*****************************************************************************
68  * Local prototypes
69  *****************************************************************************/
70 static void     InitSubtitleBlock( decoder_sys_t * p_sys );
71 static vout_thread_t *FindVout( decoder_t *);
72
73 static block_t *Reassemble( decoder_t *, block_t ** );
74
75 static void     Decode   ( decoder_t *, block_t ** );
76 static block_t *Packetize( decoder_t *, block_t ** );
77
78
79 /*****************************************************************************
80  * DecoderOpen
81  *****************************************************************************
82  * Tries to launch a decoder and return score so that the interface is able
83  * to chose.
84  *****************************************************************************/
85 static int
86 DecoderOpen( 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     InitSubtitleBlock( 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( DecoderOpen( 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  * Close:
139  *****************************************************************************/
140 static void Close( vlc_object_t *p_this )
141 {
142     decoder_t     *p_dec = (decoder_t*)p_this;
143     decoder_sys_t *p_sys = p_dec->p_sys;
144
145     dbg_print( (DECODE_DBG_CALL|DECODE_DBG_EXT) , "");
146
147     if( !p_sys->b_packetizer )
148     {
149         /* FIXME check if it's ok to not lock vout */
150         if( p_sys->p_vout != NULL && p_sys->p_vout->p_subpicture != NULL )
151         {
152             subpicture_t *  p_subpic;
153             int             i_subpic;
154
155             for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
156             {
157                 p_subpic = &p_sys->p_vout->p_subpicture[i_subpic];
158
159                 if( p_subpic != NULL &&
160                     ( ( p_subpic->i_status == RESERVED_SUBPICTURE ) ||
161                       ( p_subpic->i_status == READY_SUBPICTURE ) ) )
162                 {
163                     vout_DestroySubPicture( p_sys->p_vout, p_subpic );
164                 }
165             }
166         }
167     }
168
169     if( p_sys->p_block )
170     {
171         block_ChainRelease( p_sys->p_block );
172     }
173
174     free( p_sys );
175 }
176
177 /*****************************************************************************
178  * Decode:
179  *****************************************************************************/
180 static void
181 Decode ( decoder_t *p_dec, block_t **pp_block )
182 {
183     decoder_sys_t *p_sys = p_dec->p_sys;
184     block_t       *p_spu = Reassemble( p_dec, pp_block );
185
186     dbg_print( (DECODE_DBG_CALL) , "");
187
188     if( p_spu )
189     {
190         p_sys->i_spu = block_ChainExtract( p_spu, p_sys->buffer, 65536 );
191         p_sys->i_pts = p_spu->i_pts;
192         block_ChainRelease( p_spu );
193
194         if( ( p_sys->p_vout = FindVout( p_dec ) ) )
195         {
196             /* Parse and decode */
197             E_(ParsePacket)( p_dec );
198
199             vlc_object_release( p_sys->p_vout );
200         }
201
202         InitSubtitleBlock ( p_sys );
203     }
204
205 }
206
207 /*****************************************************************************
208  * Packetize:
209  *****************************************************************************/
210 static block_t *
211 Packetize( decoder_t *p_dec, block_t **pp_block )
212 {
213     decoder_sys_t *p_sys = p_dec->p_sys;
214     block_t       *p_spu = Reassemble( p_dec, pp_block );
215
216     if( p_spu )
217     {
218         p_spu->i_dts = p_spu->i_pts;
219         p_spu->i_length = 0;
220
221         InitSubtitleBlock( p_sys );
222
223         return block_ChainGather( p_spu );
224     }
225     return NULL;
226 }
227
228 /* following functions are local */
229
230 static void 
231 InitSubtitleData(decoder_sys_t *p_sys)
232 {
233   if ( p_sys->subtitle_data ) {
234     if ( p_sys->subtitle_data_size < p_sys->i_spu_size ) {
235       p_sys->subtitle_data = realloc(p_sys->subtitle_data,
236                                     p_sys->i_spu_size);
237       p_sys->subtitle_data_size = p_sys->i_spu_size;
238     }
239   } else {
240     p_sys->subtitle_data = malloc(p_sys->i_spu_size);
241     p_sys->subtitle_data_size = p_sys->i_spu_size;
242     /* FIXME: wrong place to get p_sys */
243     p_sys->i_image = 0;
244   }
245   p_sys->subtitle_data_pos = 0;
246 }
247
248 static void 
249 AppendData ( decoder_t *p_dec, uint8_t *buffer, uint32_t buf_len )
250 {
251   decoder_sys_t *p_sys = p_dec->p_sys;
252   int chunk_length = buf_len;
253
254   if ( chunk_length > p_sys->i_spu_size - p_sys->subtitle_data_pos ) {
255     msg_Warn( p_dec, "too much data (%d) expecting at most %u",
256               chunk_length, p_sys->i_spu_size - p_sys->subtitle_data_pos );
257
258     chunk_length = p_sys->i_spu_size - p_sys->subtitle_data_pos;
259   }
260
261   if ( chunk_length > 0 ) {
262     memcpy(p_sys->subtitle_data + p_sys->subtitle_data_pos,
263            buffer, chunk_length);
264     p_sys->subtitle_data_pos += chunk_length;
265     dbg_print(DECODE_DBG_PACKET, "%d bytes appended, pointer now %d",
266               chunk_length, p_sys->subtitle_data_pos);
267   }
268 }
269
270 /*****************************************************************************
271  InitSubtitleBlock:
272
273 Initialize so the next packet will start off a new one.
274
275  *****************************************************************************/
276 static void 
277 InitSubtitleBlock( decoder_sys_t * p_sys ) 
278 {
279   p_sys->i_spu_size = 0;
280   p_sys->state      = SUBTITLE_BLOCK_EMPTY;
281   p_sys->i_spu      = 0;
282   p_sys->p_block    = NULL;
283   p_sys->subtitle_data_pos = 0;
284
285 }
286
287 #define SPU_HEADER_LEN 5
288
289 /*****************************************************************************
290  Reassemble:
291
292  The data for single screen subtitle may come in one of many
293  non-contiguous packets of a stream. This routine is called when the
294  next packet in the stream comes in. The job of this routine is to
295  parse the header, if this is the beginning, and combine the packets
296  into one complete subtitle unit.
297
298  If everything is complete, we will return a block. Otherwise return
299  NULL.
300
301
302  The format of the beginning of the subtitle packet that is used here.
303
304    size    description
305    -------------------------------------------
306    byte    subtitle channel (0..7) in bits 0-3
307    byte    subtitle packet number of this subtitle image 0-N,
308            if the subtitle packet is complete, the top bit of the byte is 1.
309    uint16  subtitle image number
310
311  *****************************************************************************/
312 static block_t *
313 Reassemble( decoder_t *p_dec, block_t **pp_block )
314 {
315     decoder_sys_t *p_sys = p_dec->p_sys;
316     block_t *p_block;
317     uint8_t *p_buffer;
318     uint16_t i_expected_image;
319     uint8_t  i_packet, i_expected_packet;
320
321     if( pp_block == NULL || *pp_block == NULL )
322     {
323         return NULL;
324     }
325     p_block = *pp_block;
326     *pp_block = NULL;
327
328     if( p_block->i_buffer < SPU_HEADER_LEN )
329     {
330       msg_Dbg( p_dec, "invalid packet header (size %d < %d)" ,
331                p_block->i_buffer, SPU_HEADER_LEN );
332       block_Release( p_block );
333       return NULL;
334     }
335
336     p_buffer = p_block->p_buffer;
337
338     dbg_print( (DECODE_DBG_CALL|DECODE_DBG_PACKET), 
339                "header: 0x%02x 0x%02x 0x%02x 0x%02x, size: %i",
340                p_buffer[1], p_buffer[2], p_buffer[3], p_buffer[4],
341                p_block->i_buffer);
342
343     if( config_GetInt( p_dec, "spu-channel" ) != p_buffer[1] )
344       return NULL;
345
346     if ( p_sys->state == SUBTITLE_BLOCK_EMPTY ) {
347       i_expected_image  = p_sys->i_image+1;
348       i_expected_packet = 0;
349     } else {
350       i_expected_image  = p_sys->i_image;
351       i_expected_packet = p_sys->i_packet+1;
352     }
353
354     p_buffer += 2;
355
356     if ( *p_buffer & 0x80 ) {
357       p_sys->state = SUBTITLE_BLOCK_COMPLETE;
358       i_packet     = ( *p_buffer++ & 0x7F );
359     } else {
360       p_sys->state = SUBTITLE_BLOCK_PARTIAL;
361       i_packet     = *p_buffer++;
362     }
363
364     p_sys->i_image = GETINT16(p_buffer);
365
366     if ( p_sys->i_image != i_expected_image ) {
367       msg_Warn( p_dec, "expecting subtitle image %u but found %u",
368                 i_expected_image, p_sys->i_image );
369     }
370
371     if ( i_packet != i_expected_packet ) {
372       msg_Warn( p_dec, "expecting subtitle image packet %u but found %u",
373                 i_expected_packet, i_packet);
374     }
375
376     p_sys->i_packet = i_packet;
377
378     if ( p_sys->i_packet == 0 ) {
379       /* First packet in the subtitle block */
380       E_(ParseHeader)( p_dec, p_buffer, p_block );
381       InitSubtitleData(p_sys);
382     }
383
384     /* FIXME - remove append_data and use chainappend */
385     AppendData( p_dec, p_buffer, p_block->i_buffer - 5 );
386
387     block_ChainAppend( &p_sys->p_block, p_block );
388
389     p_sys->i_spu += p_block->i_buffer - SPU_HEADER_LEN;
390
391     if (p_sys->state == SUBTITLE_BLOCK_COMPLETE)
392     {
393       if( p_sys->i_spu != p_sys->i_spu_size )
394         {
395           msg_Warn( p_dec, "SPU packets size=%d should be %d",
396                    p_sys->i_spu, p_sys->i_spu_size );
397         }
398
399       dbg_print( (DECODE_DBG_PACKET),
400                  "subtitle packet complete, size=%d", p_sys->i_spu );
401
402       return p_sys->p_block;
403     }
404     return NULL;
405 }
406
407 /*****************************************************************************
408  * FindVout: Find a vout or wait for one to be created.
409  *****************************************************************************/
410 static vout_thread_t *FindVout( decoder_t *p_dec )
411 {
412     vout_thread_t *p_vout = NULL;
413
414     /* Find an available video output */
415     do
416     {
417         if( p_dec->b_die || p_dec->b_error )
418         {
419             break;
420         }
421
422         p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE );
423         if( p_vout )
424         {
425             break;
426         }
427
428         msleep( VOUT_OUTMEM_SLEEP );
429     }
430     while( 1 );
431
432     return p_vout;
433 }
434