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_access.h>
38 #include <vlc_strings.h>
39 #include <vlc_charset.h>
47 /*****************************************************************************
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;
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
;
76 if( isdigit( *psz_value
) )
78 i_subresult
= i_subresult
* 10;
79 i_subresult
+= *psz_value
- '0';
80 if( i_subfractions
!= -1 )
83 else if( *psz_value
== ':' )
85 i_result
+= i_subresult
;
86 i_result
= i_result
* 60;
89 else if( *psz_value
== '.' )
92 i_result
+= i_subresult
;
98 if( i_subfractions
== -1)
99 i_result
+= i_subresult
;
101 /* Convert to microseconds */
102 if( i_subfractions
== -1)
104 while( i_subfractions
< 6 )
106 i_subresult
= i_subresult
* 10;
109 i_result
= i_result
* 1000000;
110 if( i_subfractions
!= -1)
111 i_result
+= i_subresult
;
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
);
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
);
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
;
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" );
171 p_demux
->pf_control
= access_vaDirectoryControlHelper
;
172 p_demux
->pf_readdir
= ReadDir
;
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
;
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
;
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
)
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 ) );
222 ReadElement( p_xml_reader
, &psz_moreinfo
);
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
);
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;
241 psz_title
= input_item_GetTitle( p_current_input
);
243 psz_artist
= input_item_GetArtist( p_current_input
);
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 */
265 if( asprintf( ppsz_options
, ":start-time=%d" ,(int) i_start
/1000000 ) != -1)
270 if( asprintf( ppsz_options
+ i_options
, ":stop-time=%d",
271 (int) (i_start
+i_duration
)/1000000 ) != -1)
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
)
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 */
288 input_item_SetTitle( p_entry
, psz_name
);
290 input_item_SetArtist( p_entry
, psz_artist
);
292 input_item_SetCopyright( p_entry
, psz_copyright
);
294 input_item_SetURL( p_entry
, psz_moreinfo
);
295 if( psz_description
)
296 input_item_SetDescription( p_entry
, psz_description
);
298 p_entry
->i_duration
= i_duration
;
300 input_item_node_AppendItem( p_subitems
, p_entry
);
302 input_item_Release( p_entry
);
306 free( ppsz_options
[--i_options
] );
312 while( i_type
!= XML_READER_ENDELEM
|| strncasecmp( psz_node
, "ENTRY", 5 ) );
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
;
327 if (vlc_stream_GetSize( s
, &streamSize
) != VLC_SUCCESS
)
329 // Don't attempt to convert/store huge streams
330 if( streamSize
> 1024 * 1024 )
332 char* psz_source
= malloc( streamSize
+ 1 * sizeof( *psz_source
) );
333 if ( unlikely( psz_source
== NULL
) )
338 ssize_t i_ret
= vlc_stream_Read( s
, psz_source
+ i_read
,
339 streamSize
> 1024 ? 1024 : streamSize
);
342 assert( (size_t)i_ret
<= streamSize
);
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
)
356 stream_t
* p_stream
= vlc_stream_MemoryNew( p_demux
, (uint8_t*)psz_utf8
, strlen(psz_utf8
), false );
361 static int ReadDir( stream_t
*p_demux
, input_item_node_t
*p_subitems
)
363 if (unlikely(p_demux
->psz_url
== NULL
))
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
))
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;
383 p_xml_reader
= xml_ReaderCreate( p_demux
, p_stream
? p_stream
387 msg_Err( p_demux
, "Cannot parse ASX input file as XML");
393 i_type
= xml_ReaderNextNode( p_xml_reader
, &psz_node
);
394 if( i_type
== XML_READER_ERROR
)
397 if( i_type
== XML_READER_STARTELEM
)
401 if(!strncasecmp( psz_node
, "ASX", 3 ) )
405 msg_Err( p_demux
, "invalid root node" );
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 ) )
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
);
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
);
449 /* Base Node handler */
450 if( !strncasecmp( psz_node
, "BASE", 4 ) )
451 ReadElement( p_xml_reader
, &psz_base
);
453 /* Entry Ref Handler */
454 if( !strncasecmp( psz_node
, "ENTRYREF", 7 ) )
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
);
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
489 while( i_type
!= XML_READER_ENDELEM
|| strncasecmp( psz_node
, "ASX", 3 ) );
493 free( psz_title_asx
);
494 free( psz_entryref
);
498 xml_ReaderDelete( p_xml_reader
);
500 vlc_stream_Delete( p_stream
);