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