asx: fix mimetype and stream Peek
[vlc.git] / modules / demux / playlist / asx.c
blob41550b66f48053c176f7a1b8b0a35d95d2e64bee
1 /*****************************************************************************
2 * asx.c : ASX playlist format import
3 *****************************************************************************
4 * Copyright (C) 2005-2013 VLC authors and VideoLAN
5 * $Id$
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 *****************************************************************************/
24 /* See also:
25 * http://msdn.microsoft.com/en-us/library/windows/desktop/dd564668.aspx
28 /*****************************************************************************
29 * Preamble
30 *****************************************************************************/
31 #ifdef HAVE_CONFIG_H
32 # include "config.h"
33 #endif
35 #include <vlc_common.h>
36 #include <vlc_access.h>
37 #include <vlc_xml.h>
38 #include <vlc_strings.h>
39 #include <vlc_charset.h>
41 #include <assert.h>
42 #include <ctype.h>
43 #include <string.h>
45 #include "playlist.h"
47 /*****************************************************************************
48 * Local prototypes
49 *****************************************************************************/
50 static int ReadDir( stream_t *, input_item_node_t * );
52 static mtime_t ParseTime(xml_reader_t *p_xml_reader)
54 char *psz_value = NULL;
55 char *psz_start = NULL;
57 const char *psz_node = NULL;
58 const char *psz_txt = NULL;
60 int i_subfractions = -1;
62 int i_subresult = 0;
63 mtime_t i_result = 0;
67 psz_txt = xml_ReaderNextAttr( p_xml_reader, &psz_node );
69 while( psz_txt && strncasecmp( psz_txt, "VALUE", 5 ) );
71 psz_value = strdup( psz_node );
72 psz_start = psz_value;
74 while( *psz_value )
76 if( isdigit( *psz_value ) )
78 i_subresult = i_subresult * 10;
79 i_subresult += *psz_value - '0';
80 if( i_subfractions != -1 )
81 i_subfractions++;
83 else if( *psz_value == ':' )
85 i_result += i_subresult;
86 i_result = i_result * 60;
87 i_subresult = 0;
89 else if( *psz_value == '.' )
91 i_subfractions = 0;
92 i_result += i_subresult;
93 i_subresult = 0;
95 psz_value++;
98 if( i_subfractions == -1)
99 i_result += i_subresult;
101 /* Convert to microseconds */
102 if( i_subfractions == -1)
103 i_subfractions = 0;
104 while( i_subfractions < 6 )
106 i_subresult = i_subresult * 10;
107 i_subfractions++;
109 i_result = i_result * 1000000;
110 if( i_subfractions != -1)
111 i_result += i_subresult;
113 free( psz_start );
114 return i_result;
117 static void ReadElement( xml_reader_t *p_xml_reader, char **ppsz_txt )
119 const char *psz_node = NULL;
121 /* Read the text node */
122 xml_ReaderNextNode( p_xml_reader, &psz_node );
123 free( *ppsz_txt );
124 *ppsz_txt = strdup( psz_node );
125 vlc_xml_decode( *ppsz_txt );
127 /* Read the end element */
128 xml_ReaderNextNode( p_xml_reader, &psz_node );
129 /* TODO :
130 * Currently we don't check the agreement of start and end element
131 * This function is only used to read the element that cannot have child
132 * according to the reference.
136 static bool PeekASX( stream_t *s )
138 const uint8_t *p_peek;
139 return ( vlc_stream_Peek( s->s, &p_peek, 12 ) == 12
140 && !strncasecmp( (const char*) p_peek, "<asx version", 12 ) );
143 /*****************************************************************************
144 * Import_ASX: main import function
145 *****************************************************************************/
147 int Import_ASX( vlc_object_t *p_this )
149 stream_t *p_demux = (stream_t *)p_this;
151 CHECK_FILE(p_demux);
153 char *type = stream_MimeType( p_demux->s );
155 if( stream_HasExtension( p_demux, ".asx" )
156 || stream_HasExtension( p_demux, ".wax" )
157 || stream_HasExtension( p_demux, ".wvx" )
158 || (type != NULL && (strcasecmp(type, "video/x-ms-asf") == 0
159 || strcasecmp(type, "audio/x-ms-wax") == 0)
160 && PeekASX( p_demux ) ) )
162 msg_Dbg( p_demux, "found valid ASX playlist" );
163 free(type);
165 else
167 free(type);
168 return VLC_EGENERIC;
171 p_demux->pf_control = access_vaDirectoryControlHelper;
172 p_demux->pf_readdir = ReadDir;
173 return VLC_SUCCESS;
176 static void ProcessEntry( int *pi_n_entry, xml_reader_t *p_xml_reader,
177 input_item_node_t *p_subitems,
178 input_item_t *p_current_input, char *psz_prefix )
180 const char *psz_node = NULL;
181 const char *psz_txt = NULL;
182 int i_type;
184 char *psz_title = NULL;
185 char *psz_artist = NULL;
186 char *psz_copyright = NULL;
187 char *psz_moreinfo = NULL;
188 char *psz_description = NULL;
189 char *psz_name = NULL;
190 char *psz_mrl = NULL;
191 char *psz_href = NULL;
193 input_item_t *p_entry = NULL;
195 int i_options;
196 mtime_t i_start = 0;
197 mtime_t i_duration = 0;
198 char *ppsz_options[2];
202 i_type = xml_ReaderNextNode( p_xml_reader, &psz_node );
204 if( i_type == XML_READER_STARTELEM )
206 /* Metadata Node */
207 if( !strncasecmp( psz_node, "TITLE", 5 ) )
208 ReadElement( p_xml_reader, &psz_title );
209 else if( !strncasecmp( psz_node, "AUTHOR", 6 ) )
210 ReadElement( p_xml_reader, &psz_artist );
211 else if( !strncasecmp( psz_node, "COPYRIGHT", 9 ) )
212 ReadElement( p_xml_reader, &psz_copyright );
213 else if( !strncasecmp( psz_node,"MOREINFO", 8 ) )
217 psz_txt = xml_ReaderNextAttr( p_xml_reader, &psz_node );
219 while(psz_txt && strncasecmp( psz_txt, "HREF", 4 ) );
221 if( !psz_txt )
222 ReadElement( p_xml_reader, &psz_moreinfo );
223 else
224 psz_moreinfo = strdup( psz_node );
225 vlc_xml_decode( psz_moreinfo );
227 else if( !strncasecmp( psz_node, "ABSTRACT", 8 ) )
228 ReadElement( p_xml_reader, &psz_description );
229 else if( !strncasecmp( psz_node, "DURATION", 8 ) )
230 i_duration = ParseTime( p_xml_reader );
231 else if( !strncasecmp( psz_node, "STARTTIME", 9 ) )
232 i_start = ParseTime( p_xml_reader );
233 else
234 /* Reference Node */
235 /* All ref node will be converted into an entry */
236 if( !strncasecmp( psz_node, "REF", 3 ) )
238 *pi_n_entry = *pi_n_entry + 1;
240 if( !psz_title )
241 psz_title = input_item_GetTitle( p_current_input );
242 if( !psz_artist )
243 psz_artist = input_item_GetArtist( p_current_input );
244 if( !psz_copyright )
245 psz_copyright = input_item_GetCopyright( p_current_input );
246 if( !psz_description )
247 psz_description = input_item_GetDescription( p_current_input );
251 psz_txt = xml_ReaderNextAttr( p_xml_reader, &psz_node );
253 while( strncasecmp( psz_txt, "HREF", 4) );
254 psz_href = strdup( psz_node );
256 if( asprintf( &psz_name, "%d. %s", *pi_n_entry, psz_title ) == -1)
257 psz_name = strdup( psz_title );
258 vlc_xml_decode( psz_href );
259 psz_mrl = ProcessMRL( psz_href, psz_prefix );
261 /* Add Time information */
262 i_options = 0;
263 if( i_start )
265 if( asprintf( ppsz_options, ":start-time=%d" ,(int) i_start/1000000 ) != -1)
266 i_options++;
268 if( i_duration)
270 if( asprintf( ppsz_options + i_options, ":stop-time=%d",
271 (int) (i_start+i_duration)/1000000 ) != -1)
272 i_options++;
275 /* Create the input item */
276 p_entry = input_item_NewExt( psz_mrl, psz_name, i_duration,
277 ITEM_TYPE_UNKNOWN, ITEM_NET_UNKNOWN );
278 if( p_entry == NULL )
279 goto end;
281 input_item_AddOptions( p_entry, i_options,
282 (const char **)ppsz_options,
283 VLC_INPUT_OPTION_TRUSTED );
284 input_item_CopyOptions( p_entry, p_current_input );
286 /* Add the metadata */
287 if( psz_name )
288 input_item_SetTitle( p_entry, psz_name );
289 if( psz_artist )
290 input_item_SetArtist( p_entry, psz_artist );
291 if( psz_copyright )
292 input_item_SetCopyright( p_entry, psz_copyright );
293 if( psz_moreinfo )
294 input_item_SetURL( p_entry, psz_moreinfo );
295 if( psz_description )
296 input_item_SetDescription( p_entry, psz_description );
297 if( i_duration > 0 )
298 p_entry->i_duration = i_duration;
300 input_item_node_AppendItem( p_subitems, p_entry );
302 input_item_Release( p_entry );
304 end:
305 while( i_options )
306 free( ppsz_options[--i_options] );
307 free( psz_name );
308 free( psz_mrl );
312 while( i_type != XML_READER_ENDELEM || strncasecmp( psz_node, "ENTRY", 5 ) );
314 free( psz_href );
315 free( psz_title );
316 free( psz_artist );
317 free( psz_copyright );
318 free( psz_moreinfo );
319 free( psz_description );
322 static stream_t* UTF8Stream( stream_t *p_demux )
324 stream_t *s = p_demux->s;
325 uint64_t streamSize;
327 if (vlc_stream_GetSize( s, &streamSize ) != VLC_SUCCESS)
328 return NULL;
329 // Don't attempt to convert/store huge streams
330 if( streamSize > 1024 * 1024 )
331 return NULL;
332 char* psz_source = malloc( streamSize + 1 * sizeof( *psz_source ) );
333 if ( unlikely( psz_source == NULL ) )
334 return NULL;
335 size_t i_read = 0;
338 ssize_t i_ret = vlc_stream_Read( s, psz_source + i_read,
339 streamSize > 1024 ? 1024 : streamSize );
340 if ( i_ret <= 0 )
341 break;
342 assert( (size_t)i_ret <= streamSize );
343 streamSize -= i_ret;
344 i_read += i_ret;
345 } while ( streamSize > 0 );
346 psz_source[i_read] = 0;
347 if ( IsUTF8( psz_source ) )
348 return vlc_stream_MemoryNew( p_demux, (uint8_t*)psz_source, i_read, false );
350 char *psz_utf8 = FromLatin1( psz_source );
351 if( psz_utf8 == NULL )
353 free( psz_source );
354 return NULL;
356 stream_t * p_stream = vlc_stream_MemoryNew( p_demux, (uint8_t*)psz_utf8, strlen(psz_utf8), false );
357 free( psz_source );
358 return p_stream;
361 static int ReadDir( stream_t *p_demux, input_item_node_t *p_subitems )
363 if (unlikely(p_demux->psz_url == NULL))
364 return VLC_EGENERIC;
366 const char *psz_node = NULL;
367 char *psz_txt = NULL;
368 char *psz_base = strdup( p_demux->psz_url );
369 if (unlikely(psz_base == NULL))
370 return VLC_ENOMEM;
372 char *psz_title_asx = NULL;
373 char *psz_entryref = NULL;
375 xml_reader_t *p_xml_reader = NULL;
376 input_item_t *p_current_input = GetCurrentItem( p_demux );
377 stream_t* p_stream = UTF8Stream( p_demux );
379 bool b_first_node = false;
380 int i_type;
381 int i_n_entry = 0;
383 p_xml_reader = xml_ReaderCreate( p_demux, p_stream ? p_stream
384 : p_demux->s );
385 if( !p_xml_reader )
387 msg_Err( p_demux, "Cannot parse ASX input file as XML");
388 goto error;
393 i_type = xml_ReaderNextNode( p_xml_reader, &psz_node );
394 if( i_type == XML_READER_ERROR )
395 break;
397 if( i_type == XML_READER_STARTELEM )
399 if( !b_first_node )
401 if(!strncasecmp( psz_node, "ASX", 3 ) )
402 b_first_node = true;
403 else
405 msg_Err( p_demux, "invalid root node" );
406 goto error;
410 /* Metadata Node Handler */
411 if( !strncasecmp( psz_node, "TITLE", 5 ) )
413 ReadElement( p_xml_reader, &psz_title_asx );
414 input_item_SetTitle( p_current_input, psz_title_asx );
416 else if( !strncasecmp( psz_node, "AUTHOR", 6 ) )
418 ReadElement( p_xml_reader, &psz_txt );
419 input_item_SetArtist( p_current_input, psz_txt );
421 else if( !strncasecmp( psz_node, "COPYRIGHT", 9 ) )
423 ReadElement( p_xml_reader, &psz_txt );
424 input_item_SetCopyright( p_current_input, psz_txt );
426 else if( !strncasecmp( psz_node, "MOREINFO", 8 ) )
428 const char *psz_tmp;
431 psz_tmp = xml_ReaderNextAttr( p_xml_reader, &psz_node );
433 while( psz_tmp && strncasecmp( psz_tmp, "HREF", 4 ) );
435 if( !psz_tmp ) // If HREF attribute doesn't exist
436 ReadElement( p_xml_reader, &psz_txt );
437 else
438 psz_txt = strdup( psz_node );
440 vlc_xml_decode( psz_txt );
441 input_item_SetURL( p_current_input, psz_txt );
443 else if( !strncasecmp( psz_node, "ABSTRACT", 8 ) )
445 ReadElement( p_xml_reader, &psz_txt );
446 input_item_SetDescription( p_current_input, psz_txt );
448 else
449 /* Base Node handler */
450 if( !strncasecmp( psz_node, "BASE", 4 ) )
451 ReadElement( p_xml_reader, &psz_base );
452 else
453 /* Entry Ref Handler */
454 if( !strncasecmp( psz_node, "ENTRYREF", 7 ) )
456 const char *psz_tmp;
459 psz_tmp = xml_ReaderNextAttr( p_xml_reader, &psz_node );
461 while( psz_tmp && !strncasecmp( psz_tmp, "HREF", 4 ) );
463 /* Create new input item */
464 input_item_t *p_input;
465 psz_txt = strdup( psz_node );
466 vlc_xml_decode( psz_txt );
467 p_input = input_item_New( psz_txt, psz_title_asx );
468 input_item_CopyOptions( p_input, p_current_input );
469 input_item_node_AppendItem( p_subitems, p_input );
471 input_item_Release( p_input );
473 else
474 /* Entry Handler */
475 if( !strncasecmp( psz_node, "ENTRY", 5 ) )
477 ProcessEntry( &i_n_entry, p_xml_reader, p_subitems,
478 p_current_input, psz_base);
480 /* FIXME Unsupported elements
481 PARAM
482 EVENT
483 REPEAT
484 ENDMARK
485 STARTMARK
489 while( i_type != XML_READER_ENDELEM || strncasecmp( psz_node, "ASX", 3 ) );
491 error:
492 free( psz_base );
493 free( psz_title_asx );
494 free( psz_entryref );
495 free( psz_txt );
497 if( p_xml_reader)
498 xml_ReaderDelete( p_xml_reader );
499 if( p_stream )
500 vlc_stream_Delete( p_stream );
502 return 0;