1 /*****************************************************************************
2 * subsdec.c : SPU decoder thread
3 *****************************************************************************
4 * Copyright (C) 2000-2001 VideoLAN
5 * $Id: subsdec.c,v 1.9 2003/09/02 20:19:26 gbazin Exp $
7 * Authors: Gildas Bazin <gbazin@netcourrier.com>
8 * Samuel Hocevar <sam@zoy.org>
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.
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.
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 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
28 #include <stdlib.h> /* malloc(), free() */
29 #include <string.h> /* memcpy(), memset() */
33 #include <vlc/decoder.h>
39 /*****************************************************************************
41 *****************************************************************************/
42 static int OpenDecoder ( vlc_object_t * );
43 static int RunDecoder ( decoder_fifo_t * );
44 static int InitThread ( subsdec_thread_t * );
45 static void EndThread ( subsdec_thread_t * );
46 static vout_thread_t *FindVout( subsdec_thread_t * );
48 /*****************************************************************************
50 *****************************************************************************/
51 static char *ppsz_encodings[] = { N_("System Default"), "ASCII", "UTF-8", "",
52 "ISO-8859-1", "CP1252", "MacRoman", "MacIceland","ISO-8859-15", "",
53 "ISO-8859-2", "CP1250", "MacCentralEurope", "MacCroatian", "MacRomania", "",
54 "ISO-8859-5", "CP1251", "MacCyrillic", "MacUkraine", "KOI8-R", "KOI8-U", "KOI8-RU", "",
55 "ISO-8859-6", "CP1256", "MacArabic", "",
56 "ISO-8859-7", "CP1253", "MacGreek", "",
57 "ISO-8859-8", "CP1255", "MacHebrew", "",
58 "ISO-8859-9", "CP1254", "MacTurkish", "",
59 "ISO-8859-13", "CP1257", "",
60 "ISO-2022-JP", "ISO-2022-JP-1", "ISO-2022-JP-2", "EUC-JP", "SHIFT_JIS", "",
61 "ISO-2022-CN", "ISO-2022-CN-EXT", "EUC-CN", "EUC-TW", "BIG5", "BIG5-HKSCS", "",
62 "ISO-2022-KR", "EUC-KR", "",
63 "MacThai", "KOI8-T", "",
64 "ISO-8859-3", "ISO-8859-4", "ISO-8859-10", "ISO-8859-14", "ISO-8859-16", "",
65 "CP850", "CP862", "CP866", "CP874", "CP932", "CP949", "CP950", "CP1133", "CP1258", "",
67 "UTF-7", "UTF-16", "UTF-16BE", "UTF-16LE", "UTF-32", "UTF-32BE", "UTF-32LE",
68 "C99", "JAVA", "UCS-2", "UCS-2BE", "UCS-2LE", "UCS-4", "UCS-4BE", "UCS-4LE", "",
69 "HZ", "GBK", "GB18030", "JOHAB", "ARMSCII-8",
70 "Georgian-Academy", "Georgian-PS", "TIS-620", "MuleLao-1", "VISCII", "TCVN",
71 "HPROMAN8", "NEXTSTEP", NULL };
73 #define ENCODING_TEXT N_("Subtitles text encoding")
74 #define ENCODING_LONGTEXT N_("Change the encoding used in text subtitles")
75 #define ALIGN_TEXT N_("Subtitles justification")
76 #define ALIGN_LONGTEXT N_("Change the justification of substitles (0=center, 1=left, 2=right)")
79 set_description( _("file subtitles decoder") );
80 set_capability( "decoder", 50 );
81 set_callbacks( OpenDecoder, NULL );
82 add_category_hint( N_("Subtitles"), NULL, VLC_FALSE );
84 add_integer( "subsdec-align", 0, NULL, ALIGN_TEXT, ALIGN_LONGTEXT, VLC_TRUE );
85 #if defined(HAVE_ICONV)
86 add_string_from_list( "subsdec-encoding", N_("System Default"), ppsz_encodings, NULL, ENCODING_TEXT, ENCODING_LONGTEXT, VLC_FALSE );
90 /*****************************************************************************
91 * OpenDecoder: probe the decoder and return score
92 *****************************************************************************
93 * Tries to launch a decoder and return score so that the interface is able
95 *****************************************************************************/
96 static int OpenDecoder( vlc_object_t *p_this )
98 decoder_t *p_dec = (decoder_t*)p_this;
100 if( p_dec->p_fifo->i_fourcc != VLC_FOURCC('s','u','b','t') )
105 p_dec->pf_run = RunDecoder;
107 var_Create( p_this, "subsdec-align", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
108 #if defined(HAVE_ICONV)
109 var_Create( p_this, "subsdec-encoding", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
114 /*****************************************************************************
115 * RunDecoder: this function is called just after the thread is created
116 *****************************************************************************/
117 static int RunDecoder( decoder_fifo_t * p_fifo )
119 subsdec_thread_t * p_subsdec;
122 /* Allocate the memory needed to store the thread's structure */
123 p_subsdec = (subsdec_thread_t *)malloc( sizeof(subsdec_thread_t) );
125 if ( p_subsdec == NULL )
127 msg_Err( p_fifo, "out of memory" );
128 DecoderError( p_fifo );
133 * Initialize the thread properties
135 p_subsdec->p_vout = NULL;
136 p_subsdec->p_fifo = p_fifo;
137 #if defined(HAVE_ICONV)
138 p_subsdec->iconv_handle = (iconv_t)-1;
140 var_Get( p_subsdec->p_fifo, "subsdec-align", &val );
141 p_subsdec->i_align = val.i_int;
144 * Initialize thread and free configuration
146 p_subsdec->p_fifo->b_error = InitThread( p_subsdec );
149 * Main loop - it is not executed if an error occured during
152 if( p_fifo->i_fourcc == VLC_FOURCC('s','u','b','t') )
154 /* Here we are dealing with text subtitles */
155 #if defined(HAVE_ICONV)
156 var_Get( p_subsdec->p_fifo, "subsdec-encoding", &val );
157 if( strcmp( val.psz_string, N_("System Default") ) == 0 )
159 char *psz_charset =(char*)malloc( 100 );
160 vlc_current_charset(&psz_charset);
161 p_subsdec->iconv_handle = iconv_open( "UTF-8", psz_charset );
165 p_subsdec->iconv_handle = iconv_open( "UTF-8", val.psz_string );
167 if( p_subsdec->iconv_handle == (iconv_t)-1 )
169 msg_Warn( p_subsdec->p_fifo, "Unable to do requested conversion" );
171 free( val.psz_string);
173 while( (!p_subsdec->p_fifo->b_die) && (!p_subsdec->p_fifo->b_error) )
175 /* Find/Wait for a video output */
176 p_subsdec->p_vout = FindVout( p_subsdec );
178 if( p_subsdec->p_vout )
180 E_(ParseText)( p_subsdec );
181 vlc_object_release( p_subsdec->p_vout );
189 if( p_subsdec->p_fifo->b_error )
191 DecoderError( p_subsdec->p_fifo );
194 EndThread( p_subsdec );
199 EndThread( p_subsdec );
203 /* following functions are local */
205 /*****************************************************************************
206 * InitThread: initialize spu decoder thread
207 *****************************************************************************
208 * This function is called from RunThread and performs the second step of the
209 * initialization. It returns 0 on success. Note that the thread's flag are not
210 * modified inside this function.
211 *****************************************************************************/
212 static int InitThread( subsdec_thread_t *p_subsdec )
216 /* Call InitBitstream anyway so p_subsdec->bit_stream is in a known
217 * state before calling CloseBitstream */
218 i_ret = InitBitstream( &p_subsdec->bit_stream, p_subsdec->p_fifo,
221 /* Check for a video output */
222 p_subsdec->p_vout = FindVout( p_subsdec );
224 if( !p_subsdec->p_vout )
229 /* It was just a check */
230 vlc_object_release( p_subsdec->p_vout );
231 p_subsdec->p_vout = NULL;
236 /*****************************************************************************
237 * FindVout: Find a vout or wait for one to be created.
238 *****************************************************************************/
239 static vout_thread_t *FindVout( subsdec_thread_t *p_subsdec )
241 vout_thread_t *p_vout = NULL;
243 /* Find an available video output */
246 if( p_subsdec->p_fifo->b_die || p_subsdec->p_fifo->b_error )
251 p_vout = vlc_object_find( p_subsdec->p_fifo, VLC_OBJECT_VOUT,
259 msleep( VOUT_OUTMEM_SLEEP );
266 /*****************************************************************************
267 * EndThread: thread destruction
268 *****************************************************************************
269 * This function is called when the thread ends after a sucessful
271 *****************************************************************************/
272 static void EndThread( subsdec_thread_t *p_subsdec )
274 if( p_subsdec->p_vout != NULL
275 && p_subsdec->p_vout->p_subpicture != NULL )
277 subpicture_t * p_subpic;
280 for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
282 p_subpic = &p_subsdec->p_vout->p_subpicture[i_subpic];
284 if( p_subpic != NULL &&
285 ( ( p_subpic->i_status == RESERVED_SUBPICTURE )
286 || ( p_subpic->i_status == READY_SUBPICTURE ) ) )
288 vout_DestroySubPicture( p_subsdec->p_vout, p_subpic );
292 #if defined(HAVE_ICONV)
293 if( p_subsdec->iconv_handle != (iconv_t)-1 )
295 iconv_close( p_subsdec->iconv_handle );
298 CloseBitstream( &p_subsdec->bit_stream );
302 /*****************************************************************************
303 * ParseText: parse an text subtitle packet and send it to the video output
304 *****************************************************************************/
305 void E_(ParseText)( subsdec_thread_t *p_subsdec )
308 mtime_t i_pts, i_dts;
309 /* We cannot display a subpicture with no date */
310 i_pts = p_subsdec->bit_stream.p_pes->i_pts;
311 i_dts = p_subsdec->bit_stream.p_pes->i_dts;
314 /* Dump the packet */
315 NextDataPacket( p_subsdec->p_fifo, &p_subsdec->bit_stream );
316 msg_Warn( p_subsdec->p_fifo, "subtitle without a date" );
320 /* Check validity of packet data */
321 if( (p_subsdec->bit_stream.p_data->p_payload_end
322 - p_subsdec->bit_stream.p_data->p_payload_start) <= 0
323 || (strlen(p_subsdec->bit_stream.p_data->p_payload_start)
324 > (size_t)(p_subsdec->bit_stream.p_data->p_payload_end
325 - p_subsdec->bit_stream.p_data->p_payload_start)) )
327 /* Dump the packet */
328 NextDataPacket( p_subsdec->p_fifo, &p_subsdec->bit_stream );
329 msg_Warn( p_subsdec->p_fifo, "invalid subtitle" );
332 psz_subtitle = p_subsdec->bit_stream.p_data->p_payload_start;
334 if( psz_subtitle[0] != '\0' )
336 #if defined(HAVE_ICONV)
337 char *psz_new_subtitle, *psz_convert_buffer_out, *psz_convert_buffer_in;
338 size_t ret, inbytes_left, outbytes_left;
340 psz_new_subtitle = malloc( 6 * strlen( psz_subtitle ) * sizeof(char) );
341 psz_convert_buffer_out = psz_new_subtitle;
342 psz_convert_buffer_in = psz_subtitle;
343 inbytes_left = strlen( psz_subtitle );
344 outbytes_left = 6 * inbytes_left;
345 ret = iconv( p_subsdec->iconv_handle, &psz_convert_buffer_in,
346 &inbytes_left, &psz_convert_buffer_out, &outbytes_left );
347 *psz_convert_buffer_out = '\0';
351 msg_Warn( p_subsdec->p_fifo, "Something fishy happened during conversion" );
355 psz_subtitle = psz_new_subtitle;
358 vout_ShowTextAbsolute( p_subsdec->p_vout, psz_subtitle, NULL,
359 OSD_ALIGN_BOTTOM | p_subsdec->i_align,
360 p_subsdec->i_align ? 20 : 0, 10,
362 #if defined(HAVE_ICONV)
363 free( psz_new_subtitle );
367 /* Prepare for next time. No need to check that
368 * p_subsdec->bit_stream->p_data is valid since we check later on
369 * for b_die and b_error */
370 NextDataPacket( p_subsdec->p_fifo, &p_subsdec->bit_stream );