* ALL: separation of the SPU engine from the VOUT.
[vlc.git] / modules / codec / ogt / cvd.c
1 /*****************************************************************************
2  * cvd.c : CVD Subtitle decoder thread
3  *****************************************************************************
4  * Copyright (C) 2003, 2004 VideoLAN
5  * $Id$
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 "subtitle.h"
36 #include "cvd.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( _("CVD 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,
61                 VLC_TRUE );
62
63     add_integer( MODULE_STRING "-duration-scaling", 3, NULL,
64                  DURATION_SCALE_TEXT, DURATION_SCALE_LONGTEXT,
65                  VLC_TRUE );
66
67     add_submodule();
68     set_description( _("Chaoji VCD subtitle packetizer") );
69     set_capability( "packetizer", 50 );
70     set_callbacks( PacketizerOpen, VCDSubClose );
71 vlc_module_end();
72
73 /*****************************************************************************
74  * Local prototypes
75  *****************************************************************************/
76
77 static block_t *Reassemble( decoder_t *, block_t ** );
78 static subpicture_t *Decode( decoder_t *, block_t ** );
79 static block_t *Packetize( decoder_t *, block_t ** );
80
81 /*****************************************************************************
82  * VCDSubOpen
83  *****************************************************************************
84  * Tries to launch a decoder and return score so that the interface is able
85  * to chose.
86  *****************************************************************************/
87 static int
88 VCDSubOpen( vlc_object_t *p_this )
89 {
90     decoder_t     *p_dec = (decoder_t*)p_this;
91     decoder_sys_t *p_sys;
92
93     if( p_dec->fmt_in.i_codec != VLC_FOURCC( 'c','v','d',' ' ) )
94     {
95         return VLC_EGENERIC;
96     }
97
98
99     p_dec->p_sys = p_sys = malloc( sizeof( decoder_sys_t ) );
100
101     p_sys->i_debug       = config_GetInt( p_this, MODULE_STRING "-debug" );
102     p_sys->b_packetizer  = VLC_FALSE;
103     p_sys->p_vout        = NULL;
104     p_sys->i_image       = -1;
105     p_sys->subtitle_data = NULL;
106
107     VCDSubInitSubtitleBlock( p_sys );
108
109     es_format_Init( &p_dec->fmt_out, SPU_ES, VLC_FOURCC( 'c','v','d',' ' ) );
110
111
112     p_dec->pf_decode_sub = Decode;
113     p_dec->pf_packetize  = Packetize;
114
115     dbg_print( (DECODE_DBG_CALL|DECODE_DBG_EXT) , "");
116
117     return VLC_SUCCESS;
118 }
119
120 /*****************************************************************************
121  * PacketizerOpen
122  *****************************************************************************
123  * Tries to launch a decoder and return score so that the interface is able
124  * to chose.
125  *****************************************************************************/
126 static int PacketizerOpen( vlc_object_t *p_this )
127 {
128     decoder_t *p_dec = (decoder_t*)p_this;
129
130     if( VCDSubOpen( p_this ) )
131     {
132         return VLC_EGENERIC;
133     }
134     p_dec->p_sys->b_packetizer = VLC_TRUE;
135
136     return VLC_SUCCESS;
137 }
138
139 /*****************************************************************************
140  * Decode:
141  *****************************************************************************/
142 static subpicture_t *
143 Decode ( decoder_t *p_dec, block_t **pp_block )
144 {
145     decoder_sys_t *p_sys = p_dec->p_sys;
146     block_t       *p_spu = Reassemble( p_dec, pp_block );
147     vout_thread_t *p_last_vout = p_dec->p_sys->p_vout;
148
149     dbg_print( (DECODE_DBG_CALL) , "");
150
151     if( p_spu )
152     {
153         p_sys->i_spu = block_ChainExtract( p_spu, p_sys->buffer, 65536 );
154         p_sys->i_pts = p_spu->i_pts;
155         block_ChainRelease( p_spu );
156
157         if( ( p_sys->p_vout = VCDSubFindVout( p_dec ) ) )
158         {
159             if( p_last_vout != p_sys->p_vout )
160             {
161                 spu_Control( p_sys->p_vout->p_spu, SPU_CHANNEL_REGISTER,
162                              &p_sys->i_subpic_channel );
163             }
164
165             /* Parse and decode */
166             E_(ParsePacket)( p_dec );
167
168             vlc_object_release( p_sys->p_vout );
169         }
170
171         VCDSubInitSubtitleBlock ( p_sys );
172     }
173
174     return NULL;
175 }
176
177 /*****************************************************************************
178  * Packetize:
179  *****************************************************************************/
180 static block_t *
181 Packetize( 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     if( p_spu )
187     {
188         p_spu->i_dts = p_spu->i_pts;
189         p_spu->i_length = 0;
190
191         VCDSubInitSubtitleBlock( p_sys );
192
193         return block_ChainGather( p_spu );
194     }
195     return NULL;
196 }
197
198 /* following functions are local */
199
200 #define SPU_HEADER_LEN 1
201
202 /*****************************************************************************
203  Reassemble:
204
205  Data for single screen subtitle may come in several non-contiguous
206  packets of a stream. This routine is called when the next packet in
207  the stream comes in. The job of this routine is to parse the header,
208  if this is the beginning, and combine the packets into one complete
209  subtitle unit.
210
211  If everything is complete, we will return a block. Otherwise return
212  NULL.
213
214  *****************************************************************************/
215 static block_t *
216 Reassemble( decoder_t *p_dec, block_t **pp_block )
217 {
218     decoder_sys_t *p_sys = p_dec->p_sys;
219     block_t *p_block;
220     uint8_t *p_buffer;
221
222     if( pp_block == NULL || *pp_block == NULL )
223     {
224         return NULL;
225     }
226     p_block = *pp_block;
227     *pp_block = NULL;
228
229     if( p_block->i_buffer < SPU_HEADER_LEN )
230     {
231       msg_Dbg( p_dec, "invalid packet header (size %d < %d)" ,
232                p_block->i_buffer, SPU_HEADER_LEN );
233       block_Release( p_block );
234       return NULL;
235     }
236
237     p_buffer = p_block->p_buffer;
238
239     dbg_print( (DECODE_DBG_CALL|DECODE_DBG_PACKET),
240                "header: 0x%02x 0x%02x 0x%02x 0x%02x, 0x%02x, 0x%02x, size: %i",
241                p_buffer[1], p_buffer[2], p_buffer[3], p_buffer[4],
242                p_buffer[5], p_buffer[6],
243                p_block->i_buffer);
244
245
246     /* Attach to our input thread and see if subtitle is selected. */
247     {
248         vlc_object_t * p_input;
249         vlc_value_t val;
250
251         p_input = vlc_object_find( p_dec, VLC_OBJECT_INPUT, FIND_PARENT );
252
253         if( !p_input ) return NULL;
254
255         if( var_Get( p_input, "spu-channel", &val ) )
256         {
257           vlc_object_release( p_input );
258           return NULL;
259         }
260
261         vlc_object_release( p_input );
262
263         /* Number could be 0bd, 1bd, 2bd, 3bd for 0..3. If so
264            reduce it to 0..3.
265          */
266         if ( (val.i_int & 0xff) == 0xbd ) val.i_int >>= 8;
267
268         if( val.i_int == -1 || val.i_int != p_buffer[0] )
269           return NULL;
270     }
271
272
273     /* From the scant data on the format, there is only only way known
274        to detect the first packet in a subtitle.  The first packet
275        seems to have a valid PTS while later packets for the same
276        image don't. */
277
278     if ( p_sys->state == SUBTITLE_BLOCK_EMPTY && p_block->i_pts == 0 ) {
279       msg_Warn( p_dec,
280                 "first packet expected but no PTS present -- skipped\n");
281       return NULL;
282     }
283
284     if ( p_sys->subtitle_data_pos == 0 ) {
285       /* First packet in the subtitle block */
286       E_(ParseHeader)( p_dec, p_buffer, p_block );
287       VCDSubInitSubtitleData(p_sys);
288     }
289
290     /* FIXME - remove append_data and use chainappend */
291     VCDSubAppendData( p_dec, p_buffer + SPU_HEADER_LEN,
292                       p_block->i_buffer - SPU_HEADER_LEN );
293
294     block_ChainAppend( &p_sys->p_block, p_block );
295
296     p_sys->i_spu += p_block->i_buffer - SPU_HEADER_LEN;
297
298     if ( p_sys->subtitle_data_pos == p_sys->i_spu_size ) {
299       E_(ParseMetaInfo)( p_dec );
300       return p_sys->p_block;
301     } else {
302       /* Not last block in subtitle, so wait for another. */
303       p_sys->state = SUBTITLE_BLOCK_PARTIAL;
304     }
305
306     return NULL;
307 }