]> git.sesse.net Git - vlc/blob - modules/codec/cmml/cmml.c
Use gettext_noop() consistently
[vlc] / modules / codec / cmml / cmml.c
1 /*****************************************************************************
2  * cmml.c : CMML annotations/metadata decoder
3  *****************************************************************************
4  * Copyright (C) 2003-2004 Commonwealth Scientific and Industrial Research
5  *                         Organisation (CSIRO) Australia
6  * Copyright (C) 2004 the VideoLAN team
7  *
8  * $Id$
9  *
10  * Author: Andre Pang <Andre.Pang@csiro.au>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #include <vlc/vlc.h>
35 #include <vlc_plugin.h>
36 #include <vlc_input.h>
37 #include <vlc_codec.h>
38 #include <vlc_osd.h>
39 #include <vlc_charset.h>
40 #include <vlc_interface.h>
41 #include "xtag.h"
42
43 #undef  CMML_DEBUG
44
45 /*****************************************************************************
46  * decoder_sys_t : decoder descriptor
47  *****************************************************************************/
48 struct decoder_sys_t
49 {
50     intf_thread_t *     p_intf;
51 };
52
53 /*****************************************************************************
54  * Local prototypes
55  *****************************************************************************/
56 static int           OpenDecoder   ( vlc_object_t * );
57 static void          CloseDecoder  ( vlc_object_t * );
58
59 static subpicture_t *DecodeBlock   ( decoder_t *, block_t ** );
60
61 static void          ParseText     ( decoder_t *, block_t * );
62
63 /*****************************************************************************
64  * Exported prototypes
65  *****************************************************************************/
66 int  OpenIntf  ( vlc_object_t * );
67 void CloseIntf ( vlc_object_t * );
68
69 /*****************************************************************************
70  * Module descriptor.
71  *****************************************************************************/
72 vlc_module_begin();
73     set_description( N_("CMML annotations decoder") );
74     set_capability( "decoder", 50 );
75     set_callbacks( OpenDecoder, CloseDecoder );
76     add_shortcut( "cmml" );
77
78     add_submodule();
79         set_capability( "interface", 0 );
80         set_callbacks( OpenIntf, CloseIntf );
81 vlc_module_end();
82
83 /*****************************************************************************
84  * OpenDecoder: probe the decoder and return score
85  *****************************************************************************
86  * Tries to launch a decoder and return score so that the interface is able
87  * to chose.
88  *****************************************************************************/
89 static int OpenDecoder( vlc_object_t *p_this )
90 {
91     decoder_t *p_dec = (decoder_t*)p_this;
92     input_thread_t * p_input;
93     decoder_sys_t *p_sys;
94     vlc_value_t val;
95
96     if( p_dec->fmt_in.i_codec != VLC_FOURCC('c','m','m','l') )
97     {
98         return VLC_EGENERIC;
99     }
100
101     p_dec->pf_decode_sub = DecodeBlock;
102
103 #ifdef CMML_DEBUG
104     msg_Dbg( p_dec, "i am at %p", p_dec );
105 #endif
106
107     /* Allocate the memory needed to store the decoder's structure */
108     if( ( p_dec->p_sys = p_sys =
109           (decoder_sys_t *)malloc(sizeof(decoder_sys_t)) ) == NULL )
110     {
111         msg_Err( p_dec, "out of memory" );
112         return VLC_EGENERIC;
113     }
114
115     /* Let other interested modules know that we're a CMML decoder
116      * We have to set this variable on the input thread, because there's
117      * typically more than one decoder running so we can't find the CMML
118      * decoder succesfully with vlc_object_find.  (Any hints on how to achieve
119      * this would be rather appreciated ;) */
120     p_input = vlc_object_find( p_dec, VLC_OBJECT_INPUT, FIND_ANYWHERE );
121 #ifdef CMML_DEBUG
122     msg_Dbg( p_dec, "p_input is at %p", p_input );
123 #endif
124     val.p_address = p_dec;
125     var_Create( p_input, "has-cmml-decoder",
126                 VLC_VAR_ADDRESS|VLC_VAR_DOINHERIT );
127     if( var_Set( p_input, "has-cmml-decoder", val ) != VLC_SUCCESS )
128     {
129         msg_Dbg( p_dec, "var_Set of has-cmml-decoder failed" );
130     }
131     vlc_object_release( p_input );
132
133     /* initialise the CMML responder interface */
134     p_sys->p_intf = intf_Create( p_dec, "cmml", 0, NULL );
135     intf_RunThread( p_sys->p_intf );
136
137     return VLC_SUCCESS;
138 }
139
140 /****************************************************************************
141  * DecodeBlock: the whole thing
142  ****************************************************************************
143  * This function must be fed with complete subtitles units.
144  ****************************************************************************/
145 static subpicture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
146 {
147     subpicture_t *p_spu;
148
149     if( !pp_block || *pp_block == NULL )
150     {
151         return NULL;
152     }
153
154     ParseText( p_dec, *pp_block );
155
156     block_Release( *pp_block );
157     *pp_block = NULL;
158
159     /* allocate an empty subpicture to return.  the actual subpicture
160      * displaying is done in the DisplayAnchor function in intf.c (called from
161      * DisplayPendingAnchor, which in turn is called from the main RunIntf
162      * loop). */
163     p_spu = p_dec->pf_spu_buffer_new( p_dec );
164     if( !p_spu )
165     {
166         msg_Dbg( p_dec, "couldn't allocate new subpicture" );
167         return NULL;
168     }
169
170     return p_spu;
171 }
172
173 /*****************************************************************************
174  * CloseDecoder: clean up the decoder
175  *****************************************************************************/
176 static void CloseDecoder( vlc_object_t *p_this )
177 {
178     decoder_t *p_dec = (decoder_t *)p_this;
179     decoder_sys_t *p_sys = p_dec->p_sys;
180     intf_thread_t *p_intf;
181
182     /* Destroy the interface object/thread */
183     p_intf = vlc_object_find( p_dec, VLC_OBJECT_INTF, FIND_CHILD );
184     if( p_intf != NULL )
185     {
186 #ifdef CMML_DEBUG
187         msg_Dbg( p_dec, "CMML decoder is freeing interface thread" );
188 #endif
189         intf_StopThread( p_intf );
190         vlc_object_detach( p_intf );
191         vlc_object_release( p_intf );
192         intf_Destroy( p_intf );
193     }
194
195     p_sys->p_intf = NULL;
196
197     free( p_sys );
198 }
199
200 /*****************************************************************************
201  * ParseText: parse an text subtitle packet and send it to the video output
202  *****************************************************************************/
203 static void ParseText( decoder_t *p_dec, block_t *p_block )
204 {
205     char *psz_subtitle, *psz_cmml, *psz_url;
206     XTag *p_clip_parser, *p_anchor;
207     vlc_value_t val;
208
209     /* We cannot display a subpicture with no date */
210     if( p_block->i_pts == 0 )
211     {
212         msg_Warn( p_dec, "subtitle without a date" );
213         return;
214     }
215
216     /* Check validity of packet data */
217     if( p_block->i_buffer <= 1 ||  p_block->p_buffer[0] == '\0' )
218     {
219         msg_Warn( p_dec, "empty subtitle" );
220         return;
221     }
222
223     /* get anchor text from CMML */
224
225     /* Copy the whole CMML tag into our own buffer:
226        allocate i_buffer bytes + 1 for the terminating \0 */
227     if ( (psz_cmml = malloc( p_block->i_buffer + 1 )) == NULL )
228         return;
229     psz_cmml = memcpy( psz_cmml, p_block->p_buffer, p_block->i_buffer );
230     psz_cmml[p_block->i_buffer] = '\0'; /* terminate the string */
231 #ifdef CMML_DEBUG
232     msg_Dbg( p_dec, "psz_cmml is \"%s\"", psz_cmml );
233 #endif
234  
235     /* Parse the <clip> part of the CMML */
236     p_clip_parser = xtag_new_parse( psz_cmml, p_block->i_buffer );
237     if( !p_clip_parser )
238     {
239         msg_Warn( p_dec, "couldn't initialise <clip> parser" );
240         free( psz_cmml );
241         return;
242     }
243
244     /* Parse the anchor tag and get its contents */
245     p_anchor = xtag_first_child( p_clip_parser, "a" );
246     if( p_anchor != NULL )
247     {
248         psz_subtitle = xtag_get_pcdata( p_anchor );
249     }
250     else
251     {
252         psz_subtitle = strdup( " " );
253     }
254
255 #ifdef CMML_DEBUG
256     msg_Dbg( p_dec, "psz_subtitle is \"%s\"", psz_subtitle );
257 #endif
258
259     /* get URL from the current clip, if one exists */
260     psz_url = xtag_get_attribute( p_anchor, "href" );
261 #ifdef CMML_DEBUG
262     msg_Dbg( p_dec, "psz_url is \"%s\"", psz_url );
263 #endif
264     if( psz_url )
265     {
266         char *psz_tmp = strdup( psz_url );
267  
268         val.p_address = psz_tmp;
269         if( var_Set( p_dec, "psz-current-anchor-url", val ) != VLC_SUCCESS )
270         {
271             (void) var_Create( p_dec, "psz-current-anchor-url",
272                                VLC_VAR_ADDRESS|VLC_VAR_DOINHERIT );
273             msg_Dbg( p_dec, "creating psz-current-anchor-url" );
274             if( var_Set( p_dec, "psz-current-anchor-url", val ) != VLC_SUCCESS )
275                 msg_Dbg( p_dec, "var_Set of psz-current-anchor-url failed" );
276         }
277     }
278
279     if( psz_subtitle )
280     {
281         char *psz_tmp = strdup( psz_subtitle );
282
283         val.p_address = psz_tmp;
284         if( var_Set( p_dec, "psz-current-anchor-description", val ) != VLC_SUCCESS )
285         {
286             (void) var_Create( p_dec, "psz-current-anchor-description",
287                                VLC_VAR_ADDRESS|VLC_VAR_DOINHERIT );
288             msg_Dbg( p_dec, "creating psz-current-anchor-description" );
289             if( var_Set( p_dec, "psz-current-anchor-description", val ) != VLC_SUCCESS )
290                 msg_Dbg( p_dec, "var_Set of psz-current-anchor-description failed" );
291         }
292
293     }
294
295     free( psz_subtitle );
296     free( psz_cmml );
297     free( p_anchor );
298     free( p_clip_parser );
299     free( psz_url );
300 }
301