]> git.sesse.net Git - vlc/blob - modules/demux/playlist/asx.c
playlist/directory.c: Add a warning message in case of failure.
[vlc] / modules / demux / playlist / asx.c
1 /*****************************************************************************
2  * asx.c : ASX playlist format import
3  *****************************************************************************
4  * Copyright (C) 2005-2013 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Derk-Jan Hartman <hartman at videolan dot org>
8  *
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.
13  *
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.
18  *
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  *****************************************************************************/
23
24 /* See also:
25  * http://msdn.microsoft.com/en-us/library/windows/desktop/dd564668.aspx
26  */
27
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31 #ifdef HAVE_CONFIG_H
32 # include "config.h"
33 #endif
34
35 #include <vlc_common.h>
36 #include <vlc_demux.h>
37 #include <vlc_xml.h>
38 #include <vlc_strings.h>
39
40 #include <ctype.h>
41
42 #include "playlist.h"
43
44 struct demux_sys_t
45 {
46 };
47
48 /*****************************************************************************
49  * Local prototypes
50  *****************************************************************************/
51 static int Demux( demux_t *p_demux);
52
53 static mtime_t ParseTime(xml_reader_t *p_xml_reader)
54 {
55     char *psz_value = NULL;
56     char *psz_start = NULL;
57
58     const char *psz_node = NULL;
59     const char *psz_txt = NULL;
60
61     int i_subfractions = -1;
62
63     int i_subresult = 0;
64     mtime_t i_result = 0;
65
66     do
67     {
68         psz_txt = xml_ReaderNextAttr( p_xml_reader, &psz_node );
69     }
70     while( psz_txt && strncasecmp( psz_txt, "VALUE", 5 ) );
71
72     psz_value = strdup( psz_node );
73     psz_start = psz_value;
74
75     while( *psz_value )
76     {
77         if( isdigit( *psz_value ) )
78         {
79             i_subresult = i_subresult * 10;
80             i_subresult += *psz_value - '0';
81             if( i_subfractions != -1 )
82                 i_subfractions++;
83         }
84         else if( *psz_value == ':' )
85         {
86             i_result += i_subresult;
87             i_result = i_result * 60;
88             i_subresult = 0;
89         }
90         else if( *psz_value == '.' )
91         {
92             i_subfractions = 0;
93             i_result += i_subresult;
94             i_subresult = 0;
95         }
96         psz_value++;
97
98     }
99     if( i_subfractions == -1)
100         i_result += i_subresult;
101
102     /* Convert to microseconds */
103     if( i_subfractions == -1)
104         i_subfractions = 0;
105     while( i_subfractions < 6 )
106     {
107         i_subresult = i_subresult * 10;
108         i_subfractions++;
109     }
110     i_result = i_result * 1000000;
111     if( i_subfractions != -1)
112         i_result += i_subresult;
113
114     free( psz_start );
115     return i_result;
116 }
117
118 static void ReadElement( xml_reader_t *p_xml_reader, char **ppsz_txt )
119 {
120     const char *psz_node = NULL;
121
122     /* Read the text node */
123     xml_ReaderNextNode( p_xml_reader, &psz_node );
124     free( *ppsz_txt );
125     *ppsz_txt = strdup( psz_node );
126     resolve_xml_special_chars( *ppsz_txt );
127
128     /* Read the end element */
129     xml_ReaderNextNode( p_xml_reader, &psz_node );
130     /* TODO :
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.
134      */
135 }
136
137 static bool PeekASX( demux_t *p_demux )
138 {
139     const uint8_t *p_peek;
140     return ( stream_Peek( p_demux->s, &p_peek, 12 ) == 12
141              && !memcmp( p_peek, "<asx version", 12 ) );
142 }
143
144 /*****************************************************************************
145  * Import_ASX: main import function
146  *****************************************************************************/
147
148 int Import_ASX( vlc_object_t *p_this )
149 {
150     demux_t *p_demux = (demux_t *)p_this;
151
152     if( demux_IsPathExtension( p_demux, ".asx" ) ||
153         demux_IsPathExtension( p_demux, ".wax" ) ||
154         demux_IsPathExtension( p_demux, ".wvx" ) ||
155         (
156           ( CheckContentType( p_demux->s, "video/x-ms-asf" ) ||
157             CheckContentType( p_demux->s, "audio/x-ms-wax" ) ) && PeekASX( p_demux )
158         ) ||
159         demux_IsForced( p_demux, "asx-open" ) )
160     {
161         STANDARD_DEMUX_INIT_MSG( "found valid ASX playlist" );
162         return VLC_SUCCESS;
163     }
164     else
165         return VLC_EGENERIC;
166 }
167
168 /*****************************************************************************
169  * Deactivate: frees unused data
170  *****************************************************************************/
171
172 void Close_ASX( vlc_object_t *p_this )
173 {
174     demux_t *p_demux = (demux_t *)p_this;
175     demux_sys_t *p_sys = p_demux->p_sys;
176
177     free( p_sys );
178 }
179
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 )
183 {
184     const char *psz_node = NULL;
185     const char *psz_txt = NULL;
186     int i_type;
187
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;
196
197     input_item_t *p_entry = NULL;
198
199     int i_options;
200     mtime_t i_start = 0;
201     mtime_t i_duration = 0;
202     char *ppsz_options[2];
203
204     do
205     {
206         i_type = xml_ReaderNextNode( p_xml_reader, &psz_node );
207
208         if( i_type == XML_READER_STARTELEM )
209         {
210             /* Metadata Node */
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 ) )
218             {
219                 do
220                 {
221                     psz_txt = xml_ReaderNextAttr( p_xml_reader, &psz_node );
222                 }
223                 while(psz_txt && strncasecmp( psz_txt, "HREF", 4 ) );
224
225                 if( !psz_txt )
226                     ReadElement( p_xml_reader, &psz_moreinfo );
227                 else
228                     psz_moreinfo = strdup( psz_node );
229                 resolve_xml_special_chars( psz_moreinfo );
230             }
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 );
237
238             /* Reference Node */
239             /* All ref node will be converted into an entry */
240             if( !strncasecmp( psz_node, "REF", 3 ) )
241             {
242                 *pi_n_entry = *pi_n_entry + 1;
243
244                 if( !psz_title )
245                     psz_title = input_item_GetTitle( p_current_input );
246                 if( !psz_artist )
247                     psz_artist = input_item_GetArtist( p_current_input );
248                 if( !psz_copyright )
249                     psz_copyright = input_item_GetCopyright( p_current_input );
250                 if( !psz_description )
251                     psz_description = input_item_GetDescription( p_current_input );
252
253                 do
254                 {
255                     psz_txt = xml_ReaderNextAttr( p_xml_reader, &psz_node );
256                 }
257                 while( strncasecmp( psz_txt, "HREF", 4) );
258                 psz_href = strdup( psz_node );
259
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 );
264
265                 /* Add Time information */
266                 i_options = 0;
267                 if( i_start )
268                 {
269                     if( asprintf( ppsz_options, ":start-time=%d" ,(int) i_start/1000000 ) != -1)
270                         i_options++;
271                 }
272                 if( i_duration)
273                 {
274                     if( asprintf( ppsz_options + i_options, ":stop-time=%d",
275                                 (int) (i_start+i_duration)/1000000 ) != -1)
276                         i_options++;
277                 }
278
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 );
283
284                 /* Add the metadata */
285                 if( psz_name )
286                     input_item_SetTitle( p_entry, psz_name );
287                 if( psz_artist )
288                     input_item_SetArtist( p_entry, psz_artist );
289                 if( psz_copyright )
290                     input_item_SetCopyright( p_entry, psz_copyright );
291                 if( psz_moreinfo )
292                     input_item_SetURL( p_entry, psz_moreinfo );
293                 if( psz_description )
294                     input_item_SetDescription( p_entry, psz_description );
295                 if( i_duration > 0)
296                     input_item_SetDuration( p_entry, i_duration );
297
298                 input_item_node_AppendItem( p_subitems, p_entry );
299
300                 while( i_options )
301                     free( ppsz_options[--i_options] );
302                 free( psz_name );
303                 free( psz_mrl );
304             }
305         }
306     }
307     while( i_type != XML_READER_ENDELEM || strncasecmp( psz_node, "ENTRY", 5 ) );
308
309     free( psz_href );
310     free( psz_title );
311     free( psz_artist );
312     free( psz_copyright );
313     free( psz_moreinfo );
314     free( psz_description );
315 }
316
317 static int Demux( demux_t *p_demux )
318 {
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;
324
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;
328
329     bool b_first_node = false;
330     int i_type;
331     int i_n_entry = 0;
332
333     p_xml_reader = xml_ReaderCreate( p_demux, p_demux->s );
334     if( !p_xml_reader )
335     {
336         msg_Err( p_demux, "Cannot parse ASX input file as XML");
337         goto error;
338     }
339
340     p_subitems = input_item_node_Create( p_current_input );
341
342     do
343     {
344         i_type = xml_ReaderNextNode( p_xml_reader, &psz_node );
345         if( i_type == XML_READER_STARTELEM )
346         {
347             if( !b_first_node )
348             {
349                 if(!strncasecmp( psz_node, "ASX", 3 ) )
350                     b_first_node = true;
351                 else
352                 {
353                     msg_Err( p_demux, "invalid root node" );
354                     goto error;
355                 }
356             }
357
358             /* Metadata Node Handler */
359             if( !strncasecmp( psz_node, "TITLE", 5 ) )
360             {
361                 ReadElement( p_xml_reader, &psz_title_asx );
362                 input_item_SetTitle( p_current_input, psz_title_asx );
363             }
364             if( !strncasecmp( psz_node, "AUTHOR", 6 ) )
365             {
366                 ReadElement( p_xml_reader, &psz_txt );
367                 input_item_SetArtist( p_current_input, psz_txt );
368             }
369             if( !strncasecmp( psz_node, "COPYRIGHT", 9 ) )
370             {
371                 ReadElement( p_xml_reader, &psz_txt );
372                 input_item_SetCopyright( p_current_input, psz_txt );
373             }
374             if( !strncasecmp( psz_node, "MOREINFO", 8 ) )
375             {
376                 const char *psz_tmp;
377                 do
378                 {
379                     psz_tmp = xml_ReaderNextAttr( p_xml_reader, &psz_node );
380                 }
381                 while( psz_tmp && strncasecmp( psz_tmp, "HREF", 4 ) );
382
383                 if( !psz_tmp )  // If HREF attribute doesn't exist
384                     ReadElement( p_xml_reader, &psz_txt );
385                 else
386                     psz_txt = strdup( psz_node );
387
388                 resolve_xml_special_chars( psz_txt );
389                 input_item_SetURL( p_current_input, psz_txt );
390             }
391             if( !strncasecmp( psz_node, "ABSTRACT", 8 ) )
392             {
393                 ReadElement( p_xml_reader, &psz_txt );
394                 input_item_SetDescription( p_current_input, psz_txt );
395             }
396
397             /* Base Node handler */
398             if( !strncasecmp( psz_node, "BASE", 4 ) )
399                 ReadElement( p_xml_reader, &psz_base );
400
401             /* Entry Ref Handler */
402             if( !strncasecmp( psz_node, "ENTRYREF", 7 ) )
403             {
404                 const char *psz_tmp;
405                 do
406                 {
407                     psz_tmp = xml_ReaderNextAttr( p_xml_reader, &psz_node );
408                 }
409                 while( psz_tmp && !strncasecmp( psz_tmp, "HREF", 4 ) );
410
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 );
418
419                 vlc_gc_decref( p_input );
420             }
421
422             /* Entry Handler */
423             if( !strncasecmp( psz_node, "ENTRY", 5 ) )
424             {
425                 ProcessEntry( &i_n_entry, p_xml_reader, p_subitems,
426                               p_current_input, psz_base);
427             }
428         /* FIXME Unsupported elements
429             PARAM
430             EVENT
431             REPEAT
432             ENDMARK
433             STARTMARK
434         */
435         }
436     }
437     while( i_type != XML_READER_ENDELEM || strncasecmp( psz_node, "ASX", 3 ) );
438
439     input_item_node_PostAndDelete( p_subitems );
440     p_subitems = NULL;
441
442
443 error:
444     free( psz_base );
445     free( psz_title_asx );
446     free( psz_entryref );
447     free( psz_txt );
448
449     if( p_xml_reader)
450         xml_ReaderDelete( p_xml_reader );
451     if( p_subitems )
452         input_item_node_Delete( p_subitems );
453
454     vlc_gc_decref( p_current_input );
455
456     return 0;
457 }