1 /*****************************************************************************
2 * asx.c : ASX playlist format import
3 *****************************************************************************
4 * Copyright (C) 2005-2013 VLC authors and VideoLAN
7 * Authors: Derk-Jan Hartman <hartman at videolan dot org>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
25 * http://msdn.microsoft.com/en-us/library/windows/desktop/dd564668.aspx
28 /*****************************************************************************
30 *****************************************************************************/
35 #include <vlc_common.h>
36 #include <vlc_demux.h>
38 #include <vlc_strings.h>
48 /*****************************************************************************
50 *****************************************************************************/
51 static int Demux( demux_t *p_demux);
53 static mtime_t ParseTime(xml_reader_t *p_xml_reader)
55 char *psz_value = NULL;
56 char *psz_start = NULL;
58 const char *psz_node = NULL;
59 const char *psz_txt = NULL;
61 int i_subfractions = -1;
68 psz_txt = xml_ReaderNextAttr( p_xml_reader, &psz_node );
70 while( psz_txt && strncasecmp( psz_txt, "VALUE", 5 ) );
72 psz_value = strdup( psz_node );
73 psz_start = psz_value;
77 if( isdigit( *psz_value ) )
79 i_subresult = i_subresult * 10;
80 i_subresult += *psz_value - '0';
81 if( i_subfractions != -1 )
84 else if( *psz_value == ':' )
86 i_result += i_subresult;
87 i_result = i_result * 60;
90 else if( *psz_value == '.' )
93 i_result += i_subresult;
99 if( i_subfractions == -1)
100 i_result += i_subresult;
102 /* Convert to microseconds */
103 if( i_subfractions == -1)
105 while( i_subfractions < 6 )
107 i_subresult = i_subresult * 10;
110 i_result = i_result * 1000000;
111 if( i_subfractions != -1)
112 i_result += i_subresult;
118 static void ReadElement( xml_reader_t *p_xml_reader, char **ppsz_txt )
120 const char *psz_node = NULL;
122 /* Read the text node */
123 xml_ReaderNextNode( p_xml_reader, &psz_node );
125 *ppsz_txt = strdup( psz_node );
126 resolve_xml_special_chars( *ppsz_txt );
128 /* Read the end element */
129 xml_ReaderNextNode( p_xml_reader, &psz_node );
131 * Currently we don't check the agreement of start and end element
132 * This function is only used to read the element that cannot have child
133 * according to the reference.
137 static bool PeekASX( demux_t *p_demux )
139 const uint8_t *p_peek;
140 return ( stream_Peek( p_demux->s, &p_peek, 12 ) == 12
141 && !memcmp( p_peek, "<asx version", 12 ) );
144 /*****************************************************************************
145 * Import_ASX: main import function
146 *****************************************************************************/
148 int Import_ASX( vlc_object_t *p_this )
150 demux_t *p_demux = (demux_t *)p_this;
152 if( demux_IsPathExtension( p_demux, ".asx" ) ||
153 demux_IsPathExtension( p_demux, ".wax" ) ||
154 demux_IsPathExtension( p_demux, ".wvx" ) ||
156 ( CheckContentType( p_demux->s, "video/x-ms-asf" ) ||
157 CheckContentType( p_demux->s, "audio/x-ms-wax" ) ) && PeekASX( p_demux )
159 demux_IsForced( p_demux, "asx-open" ) )
161 STANDARD_DEMUX_INIT_MSG( "found valid ASX playlist" );
168 /*****************************************************************************
169 * Deactivate: frees unused data
170 *****************************************************************************/
172 void Close_ASX( vlc_object_t *p_this )
174 demux_t *p_demux = (demux_t *)p_this;
175 demux_sys_t *p_sys = p_demux->p_sys;
180 static void ProcessEntry( int *pi_n_entry, xml_reader_t *p_xml_reader,
181 input_item_node_t *p_subitems,
182 input_item_t *p_current_input, char *psz_prefix )
184 const char *psz_node = NULL;
185 const char *psz_txt = NULL;
188 char *psz_title = NULL;
189 char *psz_artist = NULL;
190 char *psz_copyright = NULL;
191 char *psz_moreinfo = NULL;
192 char *psz_description = NULL;
193 char *psz_name = NULL;
194 char *psz_mrl = NULL;
195 char *psz_href = NULL;
197 input_item_t *p_entry = NULL;
201 mtime_t i_duration = 0;
202 char *ppsz_options[2];
206 i_type = xml_ReaderNextNode( p_xml_reader, &psz_node );
208 if( i_type == XML_READER_STARTELEM )
211 if( !strncasecmp( psz_node, "TITLE", 5 ) )
212 ReadElement( p_xml_reader, &psz_title );
213 if( !strncasecmp( psz_node, "AUTHOR", 6 ) )
214 ReadElement( p_xml_reader, &psz_artist );
215 if( !strncasecmp( psz_node, "COPYRIGHT", 9 ) )
216 ReadElement( p_xml_reader, &psz_copyright );
217 if( !strncasecmp( psz_node,"MOREINFO", 8 ) )
221 psz_txt = xml_ReaderNextAttr( p_xml_reader, &psz_node );
223 while(psz_txt && strncasecmp( psz_txt, "HREF", 4 ) );
226 ReadElement( p_xml_reader, &psz_moreinfo );
228 psz_moreinfo = strdup( psz_node );
229 resolve_xml_special_chars( psz_moreinfo );
231 if( !strncasecmp( psz_node, "ABSTRACT", 8 ) )
232 ReadElement( p_xml_reader, &psz_description );
233 if( !strncasecmp( psz_node, "DURATION", 8 ) )
234 i_duration = ParseTime( p_xml_reader );
235 if( !strncasecmp( psz_node, "STARTTIME", 9 ) )
236 i_start = ParseTime( p_xml_reader );
239 /* All ref node will be converted into an entry */
240 if( !strncasecmp( psz_node, "REF", 3 ) )
242 *pi_n_entry = *pi_n_entry + 1;
245 psz_title = input_item_GetTitle( p_current_input );
247 psz_artist = input_item_GetArtist( p_current_input );
249 psz_copyright = input_item_GetCopyright( p_current_input );
250 if( !psz_description )
251 psz_description = input_item_GetDescription( p_current_input );
255 psz_txt = xml_ReaderNextAttr( p_xml_reader, &psz_node );
257 while( strncasecmp( psz_txt, "HREF", 4) );
258 psz_href = strdup( psz_node );
260 if( asprintf( &psz_name, "%d. %s", *pi_n_entry, psz_title ) == -1)
261 psz_name = strdup( psz_title );
262 resolve_xml_special_chars( psz_href );
263 psz_mrl = ProcessMRL( psz_href, psz_prefix );
265 /* Add Time information */
269 if( asprintf( ppsz_options, ":start-time=%d" ,(int) i_start/1000000 ) != -1)
274 if( asprintf( ppsz_options + i_options, ":stop-time=%d",
275 (int) (i_start+i_duration)/1000000 ) != -1)
279 /* Create the input item */
280 p_entry = input_item_NewExt( psz_mrl, psz_name, i_options,
281 (const char* const*) ppsz_options, VLC_INPUT_OPTION_TRUSTED, i_duration );
282 input_item_CopyOptions( p_current_input, p_entry );
284 /* Add the metadata */
286 input_item_SetTitle( p_entry, psz_name );
288 input_item_SetArtist( p_entry, psz_artist );
290 input_item_SetCopyright( p_entry, psz_copyright );
292 input_item_SetURL( p_entry, psz_moreinfo );
293 if( psz_description )
294 input_item_SetDescription( p_entry, psz_description );
296 input_item_SetDuration( p_entry, i_duration );
298 input_item_node_AppendItem( p_subitems, p_entry );
301 free( ppsz_options[--i_options] );
307 while( i_type != XML_READER_ENDELEM || strncasecmp( psz_node, "ENTRY", 5 ) );
312 free( psz_copyright );
313 free( psz_moreinfo );
314 free( psz_description );
317 static int Demux( demux_t *p_demux )
319 const char *psz_node = NULL;
320 char *psz_txt = NULL;
321 char *psz_base = FindPrefix( p_demux );
322 char *psz_title_asx = NULL;
323 char *psz_entryref = NULL;
325 xml_reader_t *p_xml_reader = NULL;
326 input_item_t *p_current_input = GetCurrentItem( p_demux );
327 input_item_node_t *p_subitems = NULL;
329 bool b_first_node = false;
333 p_xml_reader = xml_ReaderCreate( p_demux, p_demux->s );
336 msg_Err( p_demux, "Cannot parse ASX input file as XML");
340 p_subitems = input_item_node_Create( p_current_input );
344 i_type = xml_ReaderNextNode( p_xml_reader, &psz_node );
345 if( i_type == XML_READER_STARTELEM )
349 if(!strncasecmp( psz_node, "ASX", 3 ) )
353 msg_Err( p_demux, "invalid root node" );
358 /* Metadata Node Handler */
359 if( !strncasecmp( psz_node, "TITLE", 5 ) )
361 ReadElement( p_xml_reader, &psz_title_asx );
362 input_item_SetTitle( p_current_input, psz_title_asx );
364 if( !strncasecmp( psz_node, "AUTHOR", 6 ) )
366 ReadElement( p_xml_reader, &psz_txt );
367 input_item_SetArtist( p_current_input, psz_txt );
369 if( !strncasecmp( psz_node, "COPYRIGHT", 9 ) )
371 ReadElement( p_xml_reader, &psz_txt );
372 input_item_SetCopyright( p_current_input, psz_txt );
374 if( !strncasecmp( psz_node, "MOREINFO", 8 ) )
379 psz_tmp = xml_ReaderNextAttr( p_xml_reader, &psz_node );
381 while( psz_tmp && strncasecmp( psz_tmp, "HREF", 4 ) );
383 if( !psz_tmp ) // If HREF attribute doesn't exist
384 ReadElement( p_xml_reader, &psz_txt );
386 psz_txt = strdup( psz_node );
388 resolve_xml_special_chars( psz_txt );
389 input_item_SetURL( p_current_input, psz_txt );
391 if( !strncasecmp( psz_node, "ABSTRACT", 8 ) )
393 ReadElement( p_xml_reader, &psz_txt );
394 input_item_SetDescription( p_current_input, psz_txt );
397 /* Base Node handler */
398 if( !strncasecmp( psz_node, "BASE", 4 ) )
399 ReadElement( p_xml_reader, &psz_base );
401 /* Entry Ref Handler */
402 if( !strncasecmp( psz_node, "ENTRYREF", 7 ) )
407 psz_tmp = xml_ReaderNextAttr( p_xml_reader, &psz_node );
409 while( psz_tmp && !strncasecmp( psz_tmp, "HREF", 4 ) );
411 /* Create new input item */
412 input_item_t *p_input;
413 psz_txt = strdup( psz_node );
414 resolve_xml_special_chars( psz_txt );
415 p_input = input_item_New( psz_txt, psz_title_asx );
416 input_item_CopyOptions( p_current_input, p_input );
417 input_item_node_AppendItem( p_subitems, p_input );
419 vlc_gc_decref( p_input );
423 if( !strncasecmp( psz_node, "ENTRY", 5 ) )
425 ProcessEntry( &i_n_entry, p_xml_reader, p_subitems,
426 p_current_input, psz_base);
428 /* FIXME Unsupported elements
437 while( i_type != XML_READER_ENDELEM || strncasecmp( psz_node, "ASX", 3 ) );
439 input_item_node_PostAndDelete( p_subitems );
445 free( psz_title_asx );
446 free( psz_entryref );
450 xml_ReaderDelete( p_xml_reader );
452 input_item_node_Delete( p_subitems );
454 vlc_gc_decref( p_current_input );