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 VideoLAN
10 * Author: Andre Pang <Andre.Pang@csiro.au>
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.
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.
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., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
25 *****************************************************************************/
27 /*****************************************************************************
29 *****************************************************************************/
31 #include <vlc/decoder.h>
42 /*****************************************************************************
43 * decoder_sys_t : decoder descriptor
44 *****************************************************************************/
47 intf_thread_t * p_intf;
50 /*****************************************************************************
52 *****************************************************************************/
53 static int OpenDecoder ( vlc_object_t * );
54 static void CloseDecoder ( vlc_object_t * );
56 static void DecodeBlock ( decoder_t *, block_t ** );
58 static void ParseText ( decoder_t *, block_t * );
60 /*****************************************************************************
62 *****************************************************************************/
63 int E_(OpenIntf) ( vlc_object_t * );
64 void E_(CloseIntf) ( vlc_object_t * );
66 /*****************************************************************************
68 *****************************************************************************/
70 set_description( _("CMML annotations decoder") );
71 set_capability( "decoder", 50 );
72 set_callbacks( OpenDecoder, CloseDecoder );
73 add_shortcut( "cmml" );
76 set_capability( "interface", 0 );
77 set_callbacks( E_(OpenIntf), E_(CloseIntf) );
80 /*****************************************************************************
81 * OpenDecoder: probe the decoder and return score
82 *****************************************************************************
83 * Tries to launch a decoder and return score so that the interface is able
85 *****************************************************************************/
86 static int OpenDecoder( vlc_object_t *p_this )
88 decoder_t *p_dec = (decoder_t*)p_this;
89 input_thread_t * p_input;
93 if( p_dec->fmt_in.i_codec != VLC_FOURCC('c','m','m','l') )
98 p_dec->pf_decode_sub = DecodeBlock;
101 msg_Dbg( p_dec, "I am at %p", p_dec );
104 /* Allocate the memory needed to store the decoder's structure */
105 if( ( p_dec->p_sys = p_sys =
106 (decoder_sys_t *)malloc(sizeof(decoder_sys_t)) ) == NULL )
108 msg_Err( p_dec, "out of memory" );
112 /* Let other interested modules know that we're a CMML decoder
113 * We have to set this variable on the input thread, because there's
114 * typically more than one decoder running so we can't find the CMML
115 * decoder succesfully with vlc_object_find. (Any hints on how to achieve
116 * this would be rather appreciated ;) */
117 p_input = vlc_object_find( p_dec, VLC_OBJECT_INPUT, FIND_ANYWHERE );
119 msg_Dbg( p_dec, "p_input is at %p", p_input );
121 val.p_address = p_dec;
122 var_Create( p_input, "has-cmml-decoder",
123 VLC_VAR_ADDRESS|VLC_VAR_DOINHERIT );
124 if( var_Set( p_input, "has-cmml-decoder", val ) != VLC_SUCCESS )
126 msg_Dbg( p_dec, "var_Set of has-cmml-decoder failed" );
128 vlc_object_release( p_input );
130 /* initialise the CMML responder interface */
131 p_sys->p_intf = intf_Create( p_dec, "cmml" );
132 p_sys->p_intf->b_block = VLC_FALSE;
133 intf_RunThread( p_sys->p_intf );
138 /****************************************************************************
139 * DecodeBlock: the whole thing
140 ****************************************************************************
141 * This function must be fed with complete subtitles units.
142 ****************************************************************************/
143 static void DecodeBlock( decoder_t *p_dec, block_t **pp_block )
145 if( !pp_block || *pp_block == NULL )
150 ParseText( p_dec, *pp_block );
152 block_Release( *pp_block );
156 /*****************************************************************************
157 * CloseDecoder: clean up the decoder
158 *****************************************************************************/
159 static void CloseDecoder( vlc_object_t *p_this )
161 decoder_t *p_dec = (decoder_t *)p_this;
162 decoder_sys_t *p_sys = p_dec->p_sys;
163 intf_thread_t *p_intf;
165 /* Destroy the interface object/thread */
166 p_intf = vlc_object_find( p_dec, VLC_OBJECT_INTF, FIND_CHILD );
170 msg_Dbg( p_dec, "CMML decoder is freeing interface thread" );
172 intf_StopThread( p_intf );
173 vlc_object_detach( p_intf );
174 vlc_object_release( p_intf );
175 intf_Destroy( p_intf );
178 p_sys->p_intf = NULL;
183 /*****************************************************************************
184 * ParseText: parse an text subtitle packet and send it to the video output
185 *****************************************************************************/
186 static void ParseText( decoder_t *p_dec, block_t *p_block )
188 char *psz_subtitle, *psz_cmml, *psz_url;
189 XTag *p_clip_parser, *p_anchor;
192 /* We cannot display a subpicture with no date */
193 if( p_block->i_pts == 0 )
195 msg_Warn( p_dec, "subtitle without a date" );
199 /* Check validity of packet data */
200 if( p_block->i_buffer <= 1 || p_block->p_buffer[0] == '\0' )
202 msg_Warn( p_dec, "empty subtitle" );
206 /* get anchor text from CMML */
208 /* Copy the whole CMML tag into our own buffer:
209 allocate i_buffer bytes + 1 for the terminating \0 */
210 if ( (psz_cmml = malloc( p_block->i_buffer + 1 )) == NULL )
212 psz_cmml = memcpy( psz_cmml, p_block->p_buffer, p_block->i_buffer );
213 psz_cmml[p_block->i_buffer] = '\0'; /* terminate the string */
215 msg_Dbg( p_dec, "psz_cmml is \"%s\"", psz_cmml );
218 /* Parse the <clip> part of the CMML */
219 p_clip_parser = xtag_new_parse( psz_cmml, p_block->i_buffer );
222 msg_Warn( p_dec, "couldn't initialise <clip> parser" );
227 /* Parse the anchor tag and get its contents */
228 p_anchor = xtag_first_child( p_clip_parser, "a" );
229 if( p_anchor != NULL )
231 psz_subtitle = xtag_get_pcdata( p_anchor );
235 psz_subtitle = strdup( " " );
239 msg_Dbg( p_dec, "psz_subtitle is \"%s\"", psz_subtitle );
242 /* get URL from the current clip, if one exists */
243 psz_url = xtag_get_attribute( p_anchor, "href" );
245 msg_Dbg( p_dec, "psz_url is \"%s\"", psz_url );
249 char *psz_tmp = strdup( psz_url );
251 val.p_address = psz_tmp;
252 if( var_Set( p_dec, "psz-current-anchor-url", val ) != VLC_SUCCESS )
254 (void) var_Create( p_dec, "psz-current-anchor-url",
255 VLC_VAR_ADDRESS|VLC_VAR_DOINHERIT );
256 msg_Dbg( p_dec, "creating psz-current-anchor-url" );
257 if( var_Set( p_dec, "psz-current-anchor-url", val ) != VLC_SUCCESS )
258 msg_Dbg( p_dec, "var_Set of psz-current-anchor-url failed" );
264 char *psz_tmp = strdup( psz_subtitle );
266 val.p_address = psz_tmp;
267 if( var_Set( p_dec, "psz-current-anchor-description", val ) != VLC_SUCCESS )
269 (void) var_Create( p_dec, "psz-current-anchor-description",
270 VLC_VAR_ADDRESS|VLC_VAR_DOINHERIT );
271 msg_Dbg( p_dec, "creating psz-current-anchor-description" );
272 if( var_Set( p_dec, "psz-current-anchor-description", val ) != VLC_SUCCESS )
273 msg_Dbg( p_dec, "var_Set of psz-current-anchor-description failed" );
278 if( psz_subtitle ) free( psz_subtitle );
279 if( psz_cmml ) free( psz_cmml );
280 if( p_anchor ) free( p_anchor );
281 if( p_clip_parser ) free( p_clip_parser );
282 if( psz_url ) free( psz_url );