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
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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 *****************************************************************************/
27 /*****************************************************************************
29 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_input.h>
37 #include <vlc_codec.h>
39 #include <vlc_charset.h>
40 #include <vlc_interface.h>
45 /*****************************************************************************
46 * decoder_sys_t : decoder descriptor
47 *****************************************************************************/
50 intf_thread_t * p_intf;
53 /*****************************************************************************
55 *****************************************************************************/
56 static int OpenDecoder ( vlc_object_t * );
57 static void CloseDecoder ( vlc_object_t * );
59 static subpicture_t *DecodeBlock ( decoder_t *, block_t ** );
61 static void ParseText ( decoder_t *, block_t * );
63 /*****************************************************************************
65 *****************************************************************************/
66 int OpenIntf ( vlc_object_t * );
67 void CloseIntf ( vlc_object_t * );
69 /*****************************************************************************
71 *****************************************************************************/
73 set_description( N_("CMML annotations decoder") )
74 set_capability( "decoder", 50 )
75 set_callbacks( OpenDecoder, CloseDecoder )
76 add_shortcut( "cmml" )
79 set_capability( "interface", 0 )
80 set_callbacks( OpenIntf, CloseIntf )
81 add_shortcut( "cmml" )
84 /*****************************************************************************
85 * OpenDecoder: probe the decoder and return score
86 *****************************************************************************
87 * Tries to launch a decoder and return score so that the interface is able
89 *****************************************************************************/
90 static int OpenDecoder( vlc_object_t *p_this )
92 decoder_t *p_dec = (decoder_t*)p_this;
93 input_thread_t * p_input;
96 if( p_dec->fmt_in.i_codec != VLC_CODEC_CMML )
99 p_dec->pf_decode_sub = DecodeBlock;
101 /* Allocate the memory needed to store the decoder's structure */
102 if( ( p_dec->p_sys = p_sys = malloc( sizeof(*p_sys) ) ) == NULL )
105 /* Let other interested modules know that we're a CMML decoder
106 * We have to set this variable on the input thread, because there's
107 * typically more than one decoder running so we can't find the CMML
108 * decoder succesfully with vlc_object_find. (Any hints on how to achieve
109 * this would be rather appreciated ;) */
110 p_input = vlc_object_find( p_dec, VLC_OBJECT_INPUT, FIND_ANYWHERE );
116 msg_Dbg( p_dec, "p_input is at %p", p_input );
118 val.p_address = p_dec;
119 var_Create( p_input, "has-cmml-decoder",
120 VLC_VAR_ADDRESS|VLC_VAR_DOINHERIT );
122 if( var_Set( p_input, "has-cmml-decoder", val ) != VLC_SUCCESS )
123 msg_Dbg( p_dec, "var_Set of has-cmml-decoder failed" );
124 vlc_object_release( p_input );
127 /* initialise the CMML responder interface */
128 p_sys->p_intf = intf_Create( p_dec, "cmml" );
130 intf_RunThread( p_sys->p_intf );
132 p_dec->fmt_out.i_cat = SPU_ES;
133 p_dec->fmt_out.i_codec = 0;
138 /****************************************************************************
139 * DecodeBlock: the whole thing
140 ****************************************************************************
141 * This function must be fed with complete subtitles units.
142 ****************************************************************************/
143 static subpicture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
147 if( !pp_block || *pp_block == NULL )
152 ParseText( p_dec, *pp_block );
154 block_Release( *pp_block );
157 /* allocate an empty subpicture to return. the actual subpicture
158 * displaying is done in the DisplayAnchor function in intf.c (called from
159 * DisplayPendingAnchor, which in turn is called from the main RunIntf
161 p_spu = decoder_NewSubpicture( p_dec );
164 msg_Dbg( p_dec, "couldn't allocate new subpicture" );
171 /*****************************************************************************
172 * CloseDecoder: clean up the decoder
173 *****************************************************************************/
174 static void CloseDecoder( vlc_object_t *p_this )
176 decoder_t *p_dec = (decoder_t *)p_this;
177 decoder_sys_t *p_sys = p_dec->p_sys;
179 /* Destroy the interface object/thread */
180 if( p_sys->p_intf != NULL )
182 intf_thread_t *p_intf = p_sys->p_intf;
183 intf_StopThread( p_intf );
184 vlc_object_detach( p_intf );
185 vlc_object_release( p_intf );
191 /*****************************************************************************
192 * ParseText: parse an text subtitle packet and send it to the video output
193 *****************************************************************************/
194 static void ParseText( decoder_t *p_dec, block_t *p_block )
196 char *psz_subtitle, *psz_cmml, *psz_url;
197 XTag *p_clip_parser, *p_anchor;
200 /* We cannot display a subpicture with no date */
201 if( p_block->i_pts == 0 )
203 msg_Warn( p_dec, "subtitle without a date" );
207 /* Check validity of packet data */
208 if( p_block->i_buffer <= 1 || p_block->p_buffer[0] == '\0' )
210 msg_Warn( p_dec, "empty subtitle" );
214 /* get anchor text from CMML */
216 /* Copy the whole CMML tag into our own buffer:
217 allocate i_buffer bytes + 1 for the terminating \0 */
218 if( (psz_cmml = malloc( p_block->i_buffer + 1 )) == NULL )
220 memcpy( psz_cmml, p_block->p_buffer, p_block->i_buffer );
221 psz_cmml[p_block->i_buffer] = '\0'; /* terminate the string */
223 msg_Dbg( p_dec, "psz_cmml is \"%s\"", psz_cmml );
226 /* Parse the <clip> part of the CMML */
227 p_clip_parser = xtag_new_parse( psz_cmml, p_block->i_buffer );
230 msg_Warn( p_dec, "couldn't initialise <clip> parser" );
235 /* Parse the anchor tag and get its contents */
236 p_anchor = xtag_first_child( p_clip_parser, "a" );
237 if( p_anchor != NULL )
239 psz_subtitle = xtag_get_pcdata( p_anchor );
243 psz_subtitle = strdup( " " );
247 msg_Dbg( p_dec, "psz_subtitle is \"%s\"", psz_subtitle );
250 /* get URL from the current clip, if one exists */
251 psz_url = xtag_get_attribute( p_anchor, "href" );
253 msg_Dbg( p_dec, "psz_url is \"%s\"", psz_url );
257 char *psz_tmp = strdup( psz_url );
259 val.p_address = psz_tmp;
260 if( var_Set( p_dec, "psz-current-anchor-url", val ) != VLC_SUCCESS )
262 var_Create( p_dec, "psz-current-anchor-url",
263 VLC_VAR_ADDRESS | VLC_VAR_DOINHERIT );
264 msg_Dbg( p_dec, "creating psz-current-anchor-url" );
265 if( var_Set( p_dec, "psz-current-anchor-url", val ) != VLC_SUCCESS )
266 msg_Dbg( p_dec, "var_Set of psz-current-anchor-url failed" );
272 char *psz_tmp = strdup( psz_subtitle );
274 val.p_address = psz_tmp;
275 if( var_Set( p_dec, "psz-current-anchor-description", val ) != VLC_SUCCESS )
277 var_Create( p_dec, "psz-current-anchor-description",
278 VLC_VAR_ADDRESS | VLC_VAR_DOINHERIT );
279 msg_Dbg( p_dec, "creating psz-current-anchor-description" );
280 if( var_Set( p_dec, "psz-current-anchor-description", val ) != VLC_SUCCESS )
281 msg_Dbg( p_dec, "var_Set of psz-current-anchor-description failed" );
286 free( psz_subtitle );
289 free( p_clip_parser );