]> git.sesse.net Git - vlc/blob - modules/demux/playlist/ram.c
.Ram: forgotten include found by Christophe Courtaut
[vlc] / modules / demux / playlist / ram.c
1 /*****************************************************************************
2  * ram.c : RAM playlist format import
3  *****************************************************************************
4  * Copyright (C) 2009 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Srikanth Raju <srikiraju@gmail.com>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 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 General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*
25 An example:
26 rtsp://helixserver.example.com/video1.rm?rpcontextheight=250
27 &rpcontextwidth=280&rpcontexturl="http://www.example.com/relatedinfo1.html"
28 rtsp://helixserver.example.com/video2.rm?rpurl="http://www.example.com/index.html"
29 rtsp://helixserver.example.com/sample1.smil?screensize=full
30 rtsp://helixserver.example.com/audio1.rm?start=55&end=1:25
31 rtsp://helixserver.example.com/introvid.rm?title="Introduction to Streaming Media
32 Production"&author="RealNetworks, Inc."&copyright="&#169;2001, RealNetworks, Inc."
33 rtsp://helixserver.example.com/song1.rm?clipinfo="title=Artist of the Year|artist name=Your Name
34 Here|album name=My Debut|genre=Rock|copyright=2001|year=2001|comments=This one really
35 knows how to rock!"
36
37 See also:
38 http://service.real.com/help/library/guides/realone/IntroGuide/HTML/htmfiles/ramsum.htm
39 http://service.real.com/help/library/guides/realone/IntroGuide/HTML/htmfiles/ramfile.htm
40 */
41
42
43 /*****************************************************************************
44  * Preamble
45  *****************************************************************************/
46 #ifdef HAVE_CONFIG_H
47 # include "config.h"
48 #endif
49
50 #include <vlc_common.h>
51 #include <vlc_demux.h>
52 #include <vlc_charset.h>
53
54 #include <ctype.h>
55
56 #include "playlist.h"
57
58 struct demux_sys_t
59 {
60     char *psz_prefix;
61 };
62
63 /*****************************************************************************
64  * Local prototypes
65  *****************************************************************************/
66 static int Demux( demux_t *p_demux);
67 static int Control( demux_t *p_demux, int i_query, va_list args );
68 static bool ContainsURL( demux_t *p_demux );
69 static void ParseClipInfo( char * psz_clipinfo, char **ppsz_artist, char **ppsz_title,
70                            char **ppsz_album, char **ppsz_genre, char **ppsz_year,
71                            char **ppsz_cdnum, char **ppsz_comments );
72
73 /**
74  * Import_RAM: main import function
75  * @param p_this: this demux object
76  * @return VLC_SUCCESS if everything is okay
77  */
78 int Import_RAM( vlc_object_t *p_this )
79 {
80     demux_t *p_demux = (demux_t *)p_this;
81     const uint8_t *p_peek;
82     CHECK_PEEK( p_peek, 8 );
83     if(! demux_IsPathExtension( p_demux, ".ram" ) ||
84          demux_IsPathExtension( p_demux, ".rm" ) )
85         return VLC_EGENERIC;
86
87     STANDARD_DEMUX_INIT_MSG( "found valid RAM playlist" );
88     p_demux->p_sys->psz_prefix = FindPrefix( p_demux );
89
90     return VLC_SUCCESS;
91 }
92
93 /**
94  * Frees up memory on module close
95  * @param p_this: this demux object
96  */
97 void Close_RAM( vlc_object_t *p_this )
98 {
99     demux_t *p_demux = (demux_t *)p_this;
100     free( p_demux->p_sys->psz_prefix );
101     free( p_demux->p_sys );
102 }
103
104 /**
105  * Returns a UTF8 encoded version of the string
106  * @param str: input string
107  * @return pointer to UTF8 string
108  */
109 static inline char *MaybeFromLocaleDup (const char *str)
110 {
111     if (str == NULL)
112         return NULL;
113
114     return IsUTF8 (str) ? strdup (str) : FromLocaleDup (str);
115 }
116
117 /**
118  * Converts a string to UTF8 encoding
119  * @param str: input string
120  */
121 static inline void MaybeFromLocaleRep (char **str)
122 {
123     char *const orig_str = *str;
124
125     if ((orig_str != NULL) && !IsUTF8 (orig_str))
126     {
127         *str = FromLocaleDup (orig_str);
128         free (orig_str);
129     }
130 }
131
132 /**
133  * Skips blanks in a given buffer
134  * @param s: input string
135  * @param i_strlen: length of the buffer
136  */
137 static char *SkipBlanks(char *s, size_t i_strlen )
138 {
139     while( i_strlen > 0 ) {
140         switch( *s )
141         {
142             case ' ':
143             case '\t':
144             case '\r':
145             case '\n':
146                 --i_strlen;
147                 ++s;
148                 break;
149             default:
150                 i_strlen = 0;
151         }
152     }
153     return s;
154 }
155
156 /**
157  * Converts a time of format hour:minutes:sec.fraction to seconds
158  * @param s: input string
159  * @param i_strlen: length of the buffer
160  * @return time in seconds
161  */
162 static int ParseTime(char *s, size_t i_strlen)
163 {
164     // need to parse hour:minutes:sec.fraction string
165     int result = 0;
166     int val;
167     const char *end = s + i_strlen;
168     // skip leading spaces if any
169     s = SkipBlanks(s, i_strlen);
170
171     val = 0;
172     while( (s < end) && isdigit(*s) )
173     {
174         int newval = val*10 + (*s - '0');
175         if( newval < val )
176         {
177             // overflow
178             val = 0;
179             break;
180         }
181         val = newval;
182         ++s;
183     }
184     result = val;
185     s = SkipBlanks(s, end-s);
186     if( *s == ':' )
187     {
188         ++s;
189         s = SkipBlanks(s, end-s);
190         result = result * 60;
191         val = 0;
192         while( (s < end) && isdigit(*s) )
193         {
194             int newval = val*10 + (*s - '0');
195             if( newval < val )
196             {
197                 // overflow
198                 val = 0;
199                 break;
200             }
201             val = newval;
202             ++s;
203         }
204         result += val;
205         s = SkipBlanks(s, end-s);
206         if( *s == ':' )
207         {
208             ++s;
209             s = SkipBlanks(s, end-s);
210             result = result * 60;
211             val = 0;
212             while( (s < end) && isdigit(*s) )
213             {
214                 int newval = val*10 + (*s - '0');
215                 if( newval < val )
216                 {
217                     // overflow
218                     val = 0;
219                     break;
220                 }
221                 val = newval;
222                 ++s;
223             }
224             result += val;
225             // TODO: one day, we may need to parse fraction for sub-second resolution
226         }
227     }
228     return result;
229 }
230
231 /**
232  * Main demux callback function
233  * @param p_demux: this demux object
234  */
235 static int Demux( demux_t *p_demux )
236 {
237     char       *psz_line;
238     char       *psz_name = NULL;
239     char       *psz_artist = NULL, *psz_album = NULL, *psz_genre = NULL, *psz_year = NULL;
240     char       *psz_author = NULL, *psz_title = NULL, *psz_copyright = NULL, *psz_cdnum = NULL, *psz_comments = NULL;
241     int        i_parsed_duration = 0;
242     mtime_t    i_duration = -1;
243     const char **ppsz_options = NULL;
244     int        i_options = 0, i_start = 0, i_stop = 0;
245     bool b_cleanup = false;
246     input_item_t *p_input;
247
248     INIT_PLAYLIST_STUFF;
249
250     psz_line = stream_ReadLine( p_demux->s );
251     while( psz_line )
252     {
253         char *psz_parse = psz_line;
254
255         /* Skip leading tabs and spaces */
256         while( *psz_parse == ' ' || *psz_parse == '\t' ||
257                *psz_parse == '\n' || *psz_parse == '\r' ) psz_parse++;
258
259         if( *psz_parse == '#' )
260         {
261             /* Ignore comments */
262         }
263         else if( *psz_parse )
264         {
265             char *psz_mrl, *psz_option_start, *psz_option_next, *psz_temp_mrl, *psz_option;
266             char *psz_param, *psz_value;
267             if( !psz_name || !*psz_name )
268             {
269                 /* Default filename as name for relative entries
270                    TODO: Currently not used. Either remove or use */
271                 psz_name = MaybeFromLocaleDup( psz_parse );
272             }
273
274             /* Get the MRL from the file. Note that this might contain parameters of form ?param1=value1&param2=value2 in a RAM file */
275             psz_mrl = ProcessMRL( psz_parse, p_demux->p_sys->psz_prefix );
276             MaybeFromLocaleRep( &psz_mrl );
277
278             b_cleanup = true;
279             if ( !psz_mrl ) goto error;
280
281             /* We have the MRL, now we have to check for options and parse them from MRL */
282             psz_temp_mrl = strdup( psz_mrl );
283             psz_option_start = strchr( psz_temp_mrl, '?' ); /* Look for start of options */
284             if( psz_option_start )
285             {
286                 psz_option_start++;
287                 psz_option_next = psz_option_start;
288                 while( 1 ) /* Process each option */
289                 {
290                     /* Look for end of first option which maybe a & or \0 */
291                     psz_option_start = psz_option_next;
292                     psz_option_next = strchr( psz_option_start, '&' );
293                     if( psz_option_next )
294                     {
295                         *psz_option_next = '\0';
296                         psz_option_next++;
297                     }
298                     else
299                         psz_option_next = strchr( psz_option_start, '\0' );
300                     /* Quit if options are over */
301                     if( psz_option_next == psz_option_start )
302                         break;
303                     psz_option = MaybeFromLocaleDup( psz_option_start );
304                     /* If this option is screwed up, try the next one */
305                     if( !psz_option )
306                         continue;
307
308                     /* Parse out param and value */
309                     psz_param = psz_option;
310                     if( strchr( psz_option, '=' ) )
311                     {
312                         psz_value = strchr( psz_option, '=' ) + 1;
313                         *(strchr( psz_option, '=' )) = '\0';
314                     }
315                     else
316                         break;
317
318                     /* Take action based on parameter value in the below if else structure */
319                     /* TODO: Remove any quotes surrounding values if required */
320                     if( !strcmp( psz_param, "clipinfo" ) )
321                     {
322                         ParseClipInfo( psz_value, &psz_artist, &psz_title,
323                            &psz_album, &psz_genre, &psz_year,
324                            &psz_cdnum, &psz_comments ); /* clipinfo has various sub parameters, which is parsed by this function */
325                     }
326                     else if( !strcmp( psz_param, "author" ) )
327                         psz_author = strdup(psz_value);
328                     else if( !strcmp( psz_param, "start" ) )
329                     {
330                         i_start = ParseTime( strdup( psz_value ),strlen( psz_value ) );
331                         char * temp = NULL;
332                         if( i_start )
333                         {
334                             if( asprintf( &temp, ":start-time=%d", i_start ) == -1 )
335                                 *(temp) = NULL;
336                             if( temp && *temp )
337                                 INSERT_ELEM( ppsz_options, i_options, i_options, temp );
338                         }
339                     }
340                     else if( !strcmp( psz_param, "end" ) )
341                     {
342                         i_stop = ParseTime( strdup( psz_value ), strlen( psz_value ) );
343                         char * temp = NULL;
344                         if( i_stop )
345                         {
346                             if( asprintf( &temp, ":stop-time=%d", i_stop ) == -1 )
347                                 *(temp) = NULL;
348                             if( temp && *temp )
349                                 INSERT_ELEM( ppsz_options, i_options, i_options, temp );
350                         }
351                     }
352                     else if( !strcmp( psz_param, "title" ) )
353                         psz_title = strdup(psz_value);
354                     else if( !strcmp( psz_param, "copyright" ) )
355                         psz_copyright = strdup(psz_value);
356                     else
357                     {   /* TODO: insert option anyway? Currently ignores*/
358                         /* INSERT_ELEM( ppsz_options, i_options, i_options, psz_option ); */
359                     }
360
361                     free( psz_option );
362                 }
363
364                 *(strchr( psz_mrl, '?' )) = '\0'; /* Remove options from MRL because VLC can't get the file otherwise */
365             }
366
367             free( psz_temp_mrl );
368
369             /* Create the input item and pump in all the options into playlist item */
370             p_input = input_item_NewExt( p_demux, psz_mrl, psz_title, i_options, ppsz_options, 0, i_duration );
371
372             if( psz_artist && *psz_artist ) input_item_SetArtist( p_input, psz_artist );
373             if( psz_author && *psz_author ) input_item_SetPublisher( p_input, psz_author );
374             if( psz_title && *psz_title ) input_item_SetTitle( p_input, psz_title );
375             if( psz_copyright && *psz_copyright ) input_item_SetCopyright( p_input, psz_copyright );
376             if( psz_album && *psz_album ) input_item_SetAlbum( p_input, psz_album );
377             if( psz_genre && *psz_genre ) input_item_SetGenre( p_input, psz_genre );
378             if( psz_year && *psz_year ) input_item_SetDate( p_input, psz_copyright );
379             if( psz_cdnum && *psz_cdnum ) input_item_SetTrackNum( p_input, psz_cdnum );
380             if( psz_comments && *psz_comments ) input_item_SetDescription( p_input, psz_comments );
381
382             input_item_AddSubItem( p_current_input, p_input );
383             vlc_gc_decref( p_input );
384             free( psz_mrl );
385         }
386
387  error:
388         /* Fetch another line */
389         free( psz_line );
390         psz_line = stream_ReadLine( p_demux->s );
391         if( !psz_line ) b_cleanup = true;
392
393         if( b_cleanup )
394         {
395             /* Cleanup state */
396             while( i_options-- ) free( (char*)ppsz_options[i_options] );
397             FREENULL( ppsz_options );
398             FREENULL( psz_name );
399             FREENULL( psz_artist );
400             FREENULL( psz_title );
401             FREENULL( psz_author );
402             FREENULL( psz_copyright );
403             FREENULL( psz_album );
404             FREENULL( psz_genre );
405             FREENULL( psz_year );
406             FREENULL( psz_cdnum );
407             FREENULL( psz_comments );
408             i_options = 0;
409             i_parsed_duration = 0;
410             i_duration = -1;
411             i_start = 0;
412             i_stop = 0;
413             b_cleanup = false;
414         }
415     }
416     HANDLE_PLAY_AND_RELEASE;
417     var_Destroy( p_demux, "m3u-extvlcopt" );
418     return 0; /* Needed for correct operation of go back */
419 }
420
421 /**
422  * @param p_demux: This object
423  * @param i_query:
424  * @param args: List of arguments
425  */
426 static int Control( demux_t *p_demux, int i_query, va_list args )
427 {
428     VLC_UNUSED(p_demux); VLC_UNUSED(i_query); VLC_UNUSED(args);
429     return VLC_EGENERIC;
430 }
431
432 /**
433  * Parses clipinfo parameter
434  * @param psz_clipinfo: string containing the clipinfo parameter along with quotes
435  * @param ppsz_artist: Buffer to store artist name
436  * @param ppsz_title: Buffer to store title
437  * @param ppsz_album: Buffer to store album
438  * @param ppsz_genre: Buffer to store genre
439  * @param ppsz_year: Buffer to store year
440  * @param ppsz_cdnum: Buffer to store cdnum
441  * @param ppsz_comments: Buffer to store comments
442  */
443 static void ParseClipInfo( char *psz_clipinfo, char **ppsz_artist, char **ppsz_title,
444                            char **ppsz_album, char **ppsz_genre, char **ppsz_year,
445                            char **ppsz_cdnum, char **ppsz_comments )
446 {
447     char *psz_option_next, *psz_option_start, *psz_param, *psz_value, *psz_suboption;
448     char *psz_temp_clipinfo = strdup( psz_clipinfo );
449     psz_option_start = psz_clipinfo;
450     psz_option_start = strchr( psz_temp_clipinfo, '"' );
451     if( !psz_option_start )
452         return;
453
454     psz_option_start++;
455     psz_option_next = psz_option_start;
456     while( 1 ) /* Process each sub option */
457     {
458         /* Get the sub option */
459         psz_option_start = psz_option_next;
460         psz_option_next = strchr( psz_option_start, '|' );
461         if( psz_option_next )
462             *psz_option_next = '\0';
463         else
464             psz_option_next = strchr( psz_option_start, '"' );
465         if( psz_option_next )
466             *psz_option_next = '\0';
467         else
468             psz_option_next = strchr( psz_option_start, '\0' );
469         if( psz_option_next == psz_option_start )
470             break;
471
472         psz_suboption = MaybeFromLocaleDup( psz_option_start );
473         if( !psz_suboption )
474             break;
475
476         /* Parse out param and value */
477         psz_param = psz_suboption;
478         if( strchr( psz_suboption, '=' ) )
479         {
480             psz_value = strchr( psz_suboption, '=' ) + 1;
481             *( strchr( psz_suboption, '=' ) ) = '\0';
482         }
483         else
484             break;
485         /* Put into args */
486         if( !strcmp( psz_param, "artist name" ) )
487             *ppsz_artist = strdup( psz_value );
488         else if( !strcmp( psz_param, "title" ) )
489             *ppsz_title = strdup( psz_value );
490         else if( !strcmp( psz_param, "album name" ) )
491             *ppsz_album = strdup( psz_value );
492         else if( !strcmp( psz_param, "genre" ) )
493             *ppsz_genre = strdup( psz_value );
494         else if( !strcmp( psz_param, "year" ) )
495             *ppsz_year = strdup( psz_value );
496         else if( !strcmp( psz_param, "cdnum" ) )
497             *ppsz_cdnum = strdup( psz_value );
498         else if( !strcmp( psz_param, "comments" ) )
499             *ppsz_comments = strdup( psz_value );
500
501         free( psz_suboption );
502         psz_option_next++;
503     }
504
505     free( psz_temp_clipinfo );
506 }