9f21e1595a508e1fd8ddc253c9ad03342c02f587
[vlc.git] / modules / codec / subsdec / subsdec.c
1 /*****************************************************************************
2  * subsdec.c : SPU decoder thread
3  *****************************************************************************
4  * Copyright (C) 2000-2001 VideoLAN
5  * $Id: subsdec.c,v 1.5 2003/08/10 10:22:52 gbazin Exp $
6  *
7  * Authors: Gildas Bazin <gbazin@netcourrier.com>
8  *          Samuel Hocevar <sam@zoy.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <stdlib.h>                                      /* malloc(), free() */
29 #include <string.h>                                    /* memcpy(), memset() */
30
31 #include <vlc/vlc.h>
32 #include <vlc/vout.h>
33 #include <vlc/decoder.h>
34 #include <osd.h>
35
36 #include "subsdec.h"
37
38 /*****************************************************************************
39  * Local prototypes
40  *****************************************************************************/
41 static int  OpenDecoder   ( vlc_object_t * );
42 static int  RunDecoder    ( decoder_fifo_t * );
43 static int  InitThread    ( subsdec_thread_t * );
44 static void EndThread     ( subsdec_thread_t * );
45 static vout_thread_t *FindVout( subsdec_thread_t * );
46
47 /*****************************************************************************
48  * Module descriptor.
49  *****************************************************************************/
50 static char *ppsz_encodings[] = { "ASCII", "ISO-8859-1", "ISO-8859-2", "ISO-8859-3",
51     "ISO-8859-4", "ISO-8859-5", "ISO-8859-6", "ISO-8859-7", "ISO-8859-8", 
52     "ISO-8859-9", "ISO-8859-10", "ISO-8859-13", "ISO-8859-14", "ISO-8859-15",
53     "ISO-8859-16", "ISO-2022-JP", "ISO-2022-JP-1", "ISO-2022-JP-2", "ISO-2022-CN",
54     "ISO-2022-CN-EXT", "ISO-2022-KR",
55     "CP850", "CP862", "CP866", "CP874", "CP932", "CP949", "CP950", "CP1133",
56     "CP1250", "CP1251", "CP1252", "CP1253", "CP1254", "CP1255", "CP1256", "CP1257", "CP1258",
57     "MacRoman", "MacCentralEurope", "MacIceland", "MacCroatian", "MacRomania",
58     "MacCyrillic", "MacUkraine", "MacGreek", "MacTurkish", "MacHebrew", "MacArabic",
59     "MacThai", "Macintosh",
60     "UTF-7", "UTF-8", "UTF-16", "UTF-16BE", "UTF-16LE", "UTF-32", "UTF-32BE", "UTF-32LE",
61     "C99", "JAVA", "UCS-2", "UCS-2BE", "UCS-2LE", "UCS-4", "UCS-4BE", "UCS-4LE",
62     "KOI8-R", "KOI8-U", "KOI8-RU", "KOI8-T",
63     "EUC-JP", "EUC-CN", "EUC-KR", "EUC-TW",
64     "SHIFT_JIS", "HZ", "GBK", "GB18030", "BIG5", "BIG5-HKSCS", "JOHAB", "ARMSCII-8",
65     "Georgian-Academy", "Georgian-PS", "TIS-620", "MuleLao-1", "VISCII", "TCVN",
66     "HPROMAN8", "NEXTSTEP", NULL };
67
68 #define ENCODING_TEXT N_("Subtitles text encoding")
69 #define ENCODING_LONGTEXT N_("Change the encoding used in text subtitles")
70 #define ALIGN_TEXT N_("Subtitles justification")
71 #define ALIGN_LONGTEXT N_("Change the justification of substitles (0=center, 1=left, 2=right)")
72
73 vlc_module_begin();
74     set_description( _("file subtitles decoder") );
75     set_capability( "decoder", 50 );
76     set_callbacks( OpenDecoder, NULL );
77     add_category_hint( N_("Subtitles"), NULL, VLC_FALSE );
78
79     add_integer( "subsdec-align", 0, NULL, ALIGN_TEXT, ALIGN_LONGTEXT, VLC_TRUE );
80 #if defined(HAVE_ICONV)
81     add_string_from_list( "subsdec-encoding", "ISO-8859-1", ppsz_encodings, NULL, ENCODING_TEXT, ENCODING_LONGTEXT, VLC_FALSE );
82 #endif
83 vlc_module_end();
84
85 /*****************************************************************************
86  * OpenDecoder: probe the decoder and return score
87  *****************************************************************************
88  * Tries to launch a decoder and return score so that the interface is able
89  * to chose.
90  *****************************************************************************/
91 static int OpenDecoder( vlc_object_t *p_this )
92 {
93     decoder_fifo_t *p_fifo = (decoder_fifo_t*) p_this;
94
95     if( p_fifo->i_fourcc != VLC_FOURCC('s','u','b','t') )
96     {
97         return VLC_EGENERIC;
98     }
99
100     p_fifo->pf_run = RunDecoder;
101
102     var_Create( p_this, "subsdec-align", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
103 #if defined(HAVE_ICONV)
104     var_Create( p_this, "subsdec-encoding", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
105 #endif
106     return VLC_SUCCESS;
107 }
108
109 /*****************************************************************************
110  * RunDecoder: this function is called just after the thread is created
111  *****************************************************************************/
112 static int RunDecoder( decoder_fifo_t * p_fifo )
113 {
114     subsdec_thread_t *    p_subsdec;
115     vlc_value_t           val;
116
117     /* Allocate the memory needed to store the thread's structure */
118     p_subsdec = (subsdec_thread_t *)malloc( sizeof(subsdec_thread_t) );
119
120     if ( p_subsdec == NULL )
121     {
122         msg_Err( p_fifo, "out of memory" );
123         DecoderError( p_fifo );
124         return( -1 );
125     }
126
127     /*
128      * Initialize the thread properties
129      */
130     p_subsdec->p_vout = NULL;
131     p_subsdec->p_fifo = p_fifo;
132 #if defined(HAVE_ICONV)
133     p_subsdec->iconv_handle = (iconv_t)-1;
134 #endif
135     var_Get( p_subsdec->p_fifo, "subsdec-align", &val );
136     p_subsdec->i_align = val.i_int;
137
138     /*
139      * Initialize thread and free configuration
140      */
141     p_subsdec->p_fifo->b_error = InitThread( p_subsdec );
142
143     /*
144      * Main loop - it is not executed if an error occured during
145      * initialization
146      */
147     if( p_fifo->i_fourcc == VLC_FOURCC('s','u','b','t') )
148     {
149         /* Here we are dealing with text subtitles */
150 #if defined(HAVE_ICONV)
151         var_Get( p_subsdec->p_fifo, "subsdec-encoding", &val );
152         p_subsdec->iconv_handle = iconv_open( "UTF-8", val.psz_string);
153         if( p_subsdec->iconv_handle == (iconv_t)-1 )
154         {
155             msg_Warn( p_subsdec->p_fifo, "Unable to do requested conversion" );
156         }
157         free( val.psz_string);
158 #endif
159         while( (!p_subsdec->p_fifo->b_die) && (!p_subsdec->p_fifo->b_error) )
160         {
161             /* Find/Wait for a video output */
162             p_subsdec->p_vout = FindVout( p_subsdec );
163
164             if( p_subsdec->p_vout )
165             {
166                 E_(ParseText)( p_subsdec );
167                 vlc_object_release( p_subsdec->p_vout );
168             }
169         }
170     }
171
172     /*
173      * Error loop
174      */
175     if( p_subsdec->p_fifo->b_error )
176     {
177         DecoderError( p_subsdec->p_fifo );
178
179         /* End of thread */
180         EndThread( p_subsdec );
181         return -1;
182     }
183
184     /* End of thread */
185     EndThread( p_subsdec );
186     return 0;
187 }
188
189 /* following functions are local */
190
191 /*****************************************************************************
192  * InitThread: initialize spu decoder thread
193  *****************************************************************************
194  * This function is called from RunThread and performs the second step of the
195  * initialization. It returns 0 on success. Note that the thread's flag are not
196  * modified inside this function.
197  *****************************************************************************/
198 static int InitThread( subsdec_thread_t *p_subsdec )
199 {
200     int i_ret;
201
202     /* Call InitBitstream anyway so p_subsdec->bit_stream is in a known
203      * state before calling CloseBitstream */
204     i_ret = InitBitstream( &p_subsdec->bit_stream, p_subsdec->p_fifo,
205                            NULL, NULL );
206
207     /* Check for a video output */
208     p_subsdec->p_vout = FindVout( p_subsdec );
209
210     if( !p_subsdec->p_vout )
211     {
212         return -1;
213     }
214
215     /* It was just a check */
216     vlc_object_release( p_subsdec->p_vout );
217     p_subsdec->p_vout = NULL;
218
219     return i_ret;
220 }
221
222 /*****************************************************************************
223  * FindVout: Find a vout or wait for one to be created.
224  *****************************************************************************/
225 static vout_thread_t *FindVout( subsdec_thread_t *p_subsdec )
226 {
227     vout_thread_t *p_vout = NULL;
228
229     /* Find an available video output */
230     do
231     {
232         if( p_subsdec->p_fifo->b_die || p_subsdec->p_fifo->b_error )
233         {
234             break;
235         }
236
237         p_vout = vlc_object_find( p_subsdec->p_fifo, VLC_OBJECT_VOUT,
238                                   FIND_ANYWHERE );
239
240         if( p_vout )
241         {
242             break;
243         }
244
245         msleep( VOUT_OUTMEM_SLEEP );
246     }
247     while( 1 );
248
249     return p_vout;
250 }
251
252 /*****************************************************************************
253  * EndThread: thread destruction
254  *****************************************************************************
255  * This function is called when the thread ends after a sucessful
256  * initialization.
257  *****************************************************************************/
258 static void EndThread( subsdec_thread_t *p_subsdec )
259 {
260     if( p_subsdec->p_vout != NULL
261          && p_subsdec->p_vout->p_subpicture != NULL )
262     {
263         subpicture_t *  p_subpic;
264         int             i_subpic;
265
266         for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
267         {
268             p_subpic = &p_subsdec->p_vout->p_subpicture[i_subpic];
269
270             if( p_subpic != NULL &&
271               ( ( p_subpic->i_status == RESERVED_SUBPICTURE )
272              || ( p_subpic->i_status == READY_SUBPICTURE ) ) )
273             {
274                 vout_DestroySubPicture( p_subsdec->p_vout, p_subpic );
275             }
276         }
277     }
278 #if defined(HAVE_ICONV)
279     if( p_subsdec->iconv_handle != (iconv_t)-1 )
280     {
281         iconv_close( p_subsdec->iconv_handle );
282     }
283 #endif
284     CloseBitstream( &p_subsdec->bit_stream );
285     free( p_subsdec );
286 }
287
288 /*****************************************************************************
289  * ParseText: parse an text subtitle packet and send it to the video output
290  *****************************************************************************/
291 void E_(ParseText)( subsdec_thread_t *p_subsdec )
292 {
293     char         * psz_subtitle;
294     mtime_t        i_pts, i_dts;
295     /* We cannot display a subpicture with no date */
296     i_pts = p_subsdec->bit_stream.p_pes->i_pts;
297     i_dts = p_subsdec->bit_stream.p_pes->i_dts;
298     if( i_pts == 0 )
299     {
300         /* Dump the packet */
301         NextDataPacket( p_subsdec->p_fifo, &p_subsdec->bit_stream );
302         msg_Warn( p_subsdec->p_fifo, "subtitle without a date" );
303         return;
304     }
305
306     /* Check validity of packet data */
307     if( (p_subsdec->bit_stream.p_data->p_payload_end
308           - p_subsdec->bit_stream.p_data->p_payload_start) <= 0
309         || (strlen(p_subsdec->bit_stream.p_data->p_payload_start)
310             > (size_t)(p_subsdec->bit_stream.p_data->p_payload_end
311                         - p_subsdec->bit_stream.p_data->p_payload_start)) )
312     {
313         /* Dump the packet */
314         NextDataPacket( p_subsdec->p_fifo, &p_subsdec->bit_stream );
315         msg_Warn( p_subsdec->p_fifo, "invalid subtitle" );
316         return;
317     }
318     psz_subtitle = p_subsdec->bit_stream.p_data->p_payload_start;
319
320     if( psz_subtitle[0] != '\0' )
321     {
322 #if defined(HAVE_ICONV)
323         char *psz_new_subtitle, *psz_convert_buffer_out, *psz_convert_buffer_in;
324         size_t ret, inbytes_left, outbytes_left;
325
326         psz_new_subtitle = malloc( 6 * strlen( psz_subtitle ) * sizeof(char) );
327         psz_convert_buffer_out = psz_new_subtitle;
328         psz_convert_buffer_in = psz_subtitle;
329         inbytes_left = strlen( psz_subtitle );
330         outbytes_left = 6 * inbytes_left;
331         ret = iconv( p_subsdec->iconv_handle, &psz_convert_buffer_in,
332                      &inbytes_left, &psz_convert_buffer_out, &outbytes_left );
333         *psz_convert_buffer_out = '\0';
334
335         if( inbytes_left )
336         {
337             msg_Warn( p_subsdec->p_fifo, "Something fishy happened during conversion" );
338         }
339         else
340         {
341             msg_Dbg( p_subsdec->p_fifo, "reencoded \"%s\" into \"%s\"", psz_subtitle, psz_new_subtitle );
342             psz_subtitle = psz_new_subtitle;
343         }
344 #endif
345         vout_ShowTextAbsolute( p_subsdec->p_vout, psz_subtitle, NULL, 
346                                OSD_ALIGN_BOTTOM | p_subsdec->i_align,
347                                p_subsdec->i_align ? 20 : 0, 10, 
348                                i_pts, i_dts );
349 #if defined(HAVE_ICONV)
350         free( psz_new_subtitle );
351 #endif
352     }
353
354     /* Prepare for next time. No need to check that
355      * p_subsdec->bit_stream->p_data is valid since we check later on
356      * for b_die and b_error */
357     NextDataPacket( p_subsdec->p_fifo, &p_subsdec->bit_stream );
358 }