]> git.sesse.net Git - vlc/blob - modules/demux/playlist/ram.c
Use var_Inherit* instead of var_CreateGet*.
[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_url.h>
53 #include <vlc_charset.h>
54
55 #include "playlist.h"
56
57 struct demux_sys_t
58 {
59     char *psz_prefix;
60 };
61
62 /*****************************************************************************
63  * Local prototypes
64  *****************************************************************************/
65 static int Demux( demux_t *p_demux);
66 static int Control( demux_t *p_demux, int i_query, va_list args );
67 static void ParseClipInfo( const char * psz_clipinfo, char **ppsz_artist, char **ppsz_title,
68                            char **ppsz_album, char **ppsz_genre, char **ppsz_year,
69                            char **ppsz_cdnum, char **ppsz_comments );
70
71 /**
72  * Import_RAM: main import function
73  * @param p_this: this demux object
74  * @return VLC_SUCCESS if everything is okay
75  */
76 int Import_RAM( vlc_object_t *p_this )
77 {
78     demux_t *p_demux = (demux_t *)p_this;
79
80     if(! demux_IsPathExtension( p_demux, ".ram" ) ||
81          demux_IsPathExtension( p_demux, ".rm" ) )
82         return VLC_EGENERIC;
83
84     STANDARD_DEMUX_INIT_MSG( "found valid RAM playlist" );
85     p_demux->p_sys->psz_prefix = FindPrefix( p_demux );
86
87     return VLC_SUCCESS;
88 }
89
90 /**
91  * Frees up memory on module close
92  * @param p_this: this demux object
93  */
94 void Close_RAM( vlc_object_t *p_this )
95 {
96     demux_t *p_demux = (demux_t *)p_this;
97     free( p_demux->p_sys->psz_prefix );
98     free( p_demux->p_sys );
99 }
100
101 /**
102  * Skips blanks in a given buffer
103  * @param s: input string
104  * @param i_strlen: length of the buffer
105  */
106 static const char *SkipBlanks( const char *s, size_t i_strlen )
107 {
108     while( i_strlen > 0 ) {
109         switch( *s )
110         {
111             case ' ':
112             case '\t':
113             case '\r':
114             case '\n':
115                 --i_strlen;
116                 ++s;
117                 break;
118             default:
119                 i_strlen = 0;
120         }
121     }
122     return s;
123 }
124
125 /**
126  * Converts a time of format hour:minutes:sec.fraction to seconds
127  * @param s: input string
128  * @param i_strlen: length of the buffer
129  * @return time in seconds
130  */
131 static int ParseTime( const char *s, size_t i_strlen)
132 {
133     // need to parse hour:minutes:sec.fraction string
134     int result = 0;
135     int val;
136     const char *end = s + i_strlen;
137     // skip leading spaces if any
138     s = SkipBlanks(s, i_strlen);
139
140     val = 0;
141     while( (s < end) && isdigit(*s) )
142     {
143         int newval = val*10 + (*s - '0');
144         if( newval < val )
145         {
146             // overflow
147             val = 0;
148             break;
149         }
150         val = newval;
151         ++s;
152     }
153     result = val;
154     s = SkipBlanks(s, end-s);
155     if( *s == ':' )
156     {
157         ++s;
158         s = SkipBlanks(s, end-s);
159         result = result * 60;
160         val = 0;
161         while( (s < end) && isdigit(*s) )
162         {
163             int newval = val*10 + (*s - '0');
164             if( newval < val )
165             {
166                 // overflow
167                 val = 0;
168                 break;
169             }
170             val = newval;
171             ++s;
172         }
173         result += val;
174         s = SkipBlanks(s, end-s);
175         if( *s == ':' )
176         {
177             ++s;
178             s = SkipBlanks(s, end-s);
179             result = result * 60;
180             val = 0;
181             while( (s < end) && isdigit(*s) )
182             {
183                 int newval = val*10 + (*s - '0');
184                 if( newval < val )
185                 {
186                     // overflow
187                     val = 0;
188                     break;
189                 }
190                 val = newval;
191                 ++s;
192             }
193             result += val;
194             // TODO: one day, we may need to parse fraction for sub-second resolution
195         }
196     }
197     return result;
198 }
199
200 /**
201  * Main demux callback function
202  * @param p_demux: this demux object
203  */
204 static int Demux( demux_t *p_demux )
205 {
206     char       *psz_line;
207     char       *psz_artist = NULL, *psz_album = NULL, *psz_genre = NULL, *psz_year = NULL;
208     char       *psz_author = NULL, *psz_title = NULL, *psz_copyright = NULL, *psz_cdnum = NULL, *psz_comments = NULL;
209     int        i_parsed_duration = 0;
210     mtime_t    i_duration = -1;
211     const char **ppsz_options = NULL;
212     int        i_options = 0, i_start = 0, i_stop = 0;
213     bool b_cleanup = false;
214     input_item_t *p_input;
215
216     input_item_t *p_current_input = GetCurrentItem(p_demux);
217
218     input_item_node_t *p_subitems = input_item_node_Create( p_current_input );
219
220     psz_line = stream_ReadLine( p_demux->s );
221     while( psz_line )
222     {
223         char *psz_parse = psz_line;
224
225         /* Skip leading tabs and spaces */
226         while( *psz_parse == ' ' || *psz_parse == '\t' ||
227                *psz_parse == '\n' || *psz_parse == '\r' ) psz_parse++;
228
229         if( *psz_parse == '#' )
230         {
231             /* Ignore comments */
232         }
233         else if( *psz_parse )
234         {
235             char *psz_mrl, *psz_option_next, *psz_option;
236             char *psz_param, *psz_value;
237
238             /* Get the MRL from the file. Note that this might contain parameters of form ?param1=value1&param2=value2 in a RAM file */
239             psz_mrl = ProcessMRL( psz_parse, p_demux->p_sys->psz_prefix );
240
241             b_cleanup = true;
242             if ( !psz_mrl ) goto error;
243
244             /* We have the MRL, now we have to check for options and parse them from MRL */
245             psz_option = strchr( psz_mrl, '?' ); /* Look for start of options */
246             if( psz_option )
247             {
248                 /* Remove options from MRL
249                    because VLC can't get the file otherwise */
250                 *psz_option = '\0';
251                 psz_option++;
252                 psz_option_next = psz_option;
253                 while( 1 ) /* Process each option */
254                 {
255                     /* Look for end of first option which maybe a & or \0 */
256                     psz_option = psz_option_next;
257                     psz_option_next = strchr( psz_option, '&' );
258                     if( psz_option_next )
259                     {
260                         *psz_option_next = '\0';
261                         psz_option_next++;
262                     }
263                     else
264                         psz_option_next = strchr( psz_option, '\0' );
265                     /* Quit if options are over */
266                     if( psz_option_next == psz_option )
267                         break;
268
269                     /* Parse out param and value */
270                     psz_param = psz_option;
271                     psz_value = strchr( psz_option, '=' );
272                     if( psz_value == NULL )
273                         break;
274                     *psz_value = '\0';
275                     psz_value++;
276
277                     /* Take action based on parameter value in the below if else structure */
278                     /* TODO: Remove any quotes surrounding values if required */
279                     if( !strcmp( psz_param, "clipinfo" ) )
280                     {
281                         ParseClipInfo( psz_value, &psz_artist, &psz_title,
282                            &psz_album, &psz_genre, &psz_year,
283                            &psz_cdnum, &psz_comments ); /* clipinfo has various sub parameters, which is parsed by this function */
284                     }
285                     else if( !strcmp( psz_param, "author" ) )
286                     {
287                         psz_author = decode_URI_duplicate(psz_value);
288                         EnsureUTF8( psz_author );
289                     }
290                     else if( !strcmp( psz_param, "start" ) )
291                     {
292                         i_start = ParseTime( psz_value, strlen( psz_value ) );
293                         char *temp;
294                         if( i_start )
295                         {
296                             if( asprintf( &temp, ":start-time=%d", i_start ) != -1 )
297                                 INSERT_ELEM( ppsz_options, i_options, i_options, temp );
298                         }
299                     }
300                     else if( !strcmp( psz_param, "end" ) )
301                     {
302                         i_stop = ParseTime( psz_value, strlen( psz_value ) );
303                         char *temp;
304                         if( i_stop )
305                         {
306                             if( asprintf( &temp, ":stop-time=%d", i_stop ) != -1 )
307                                 INSERT_ELEM( ppsz_options, i_options, i_options, temp );
308                         }
309                     }
310                     else if( !strcmp( psz_param, "title" ) )
311                     {
312                         psz_title = decode_URI_duplicate(psz_value);
313                         EnsureUTF8( psz_title );
314                     }
315                     else if( !strcmp( psz_param, "copyright" ) )
316                     {
317                         psz_copyright = decode_URI_duplicate(psz_value);
318                         EnsureUTF8( psz_copyright );
319                     }
320                     else
321                     {   /* TODO: insert option anyway? Currently ignores*/
322                         /* INSERT_ELEM( ppsz_options, i_options, i_options, psz_option ); */
323                     }
324                 }
325             }
326
327             /* Create the input item and pump in all the options into playlist item */
328             p_input = input_item_NewExt( p_demux, psz_mrl, psz_title, i_options, ppsz_options, 0, i_duration );
329
330             if( !EMPTY_STR( psz_artist ) ) input_item_SetArtist( p_input, psz_artist );
331             if( !EMPTY_STR( psz_author ) ) input_item_SetPublisher( p_input, psz_author );
332             if( !EMPTY_STR( psz_title ) ) input_item_SetTitle( p_input, psz_title );
333             if( !EMPTY_STR( psz_copyright ) ) input_item_SetCopyright( p_input, psz_copyright );
334             if( !EMPTY_STR( psz_album ) ) input_item_SetAlbum( p_input, psz_album );
335             if( !EMPTY_STR( psz_genre ) ) input_item_SetGenre( p_input, psz_genre );
336             if( !EMPTY_STR( psz_year ) ) input_item_SetDate( p_input, psz_copyright );
337             if( !EMPTY_STR( psz_cdnum ) ) input_item_SetTrackNum( p_input, psz_cdnum );
338             if( !EMPTY_STR( psz_comments ) ) input_item_SetDescription( p_input, psz_comments );
339
340             input_item_node_AppendItem( p_subitems, p_input );
341             vlc_gc_decref( p_input );
342             free( psz_mrl );
343         }
344
345  error:
346         /* Fetch another line */
347         free( psz_line );
348         psz_line = stream_ReadLine( p_demux->s );
349         if( !psz_line ) b_cleanup = true;
350
351         if( b_cleanup )
352         {
353             /* Cleanup state */
354             while( i_options-- ) free( (char*)ppsz_options[i_options] );
355             FREENULL( ppsz_options );
356             FREENULL( psz_artist );
357             FREENULL( psz_title );
358             FREENULL( psz_author );
359             FREENULL( psz_copyright );
360             FREENULL( psz_album );
361             FREENULL( psz_genre );
362             FREENULL( psz_year );
363             FREENULL( psz_cdnum );
364             FREENULL( psz_comments );
365             i_options = 0;
366             i_parsed_duration = 0;
367             i_duration = -1;
368             i_start = 0;
369             i_stop = 0;
370             b_cleanup = false;
371         }
372     }
373     input_item_node_PostAndDelete( p_subitems );
374     vlc_gc_decref(p_current_input);
375     var_Destroy( p_demux, "m3u-extvlcopt" );
376     return 0; /* Needed for correct operation of go back */
377 }
378
379 /**
380  * @param p_demux: This object
381  * @param i_query:
382  * @param args: List of arguments
383  */
384 static int Control( demux_t *p_demux, int i_query, va_list args )
385 {
386     VLC_UNUSED(p_demux); VLC_UNUSED(i_query); VLC_UNUSED(args);
387     return VLC_EGENERIC;
388 }
389
390 /**
391  * Parses clipinfo parameter
392  * @param psz_clipinfo: string containing the clipinfo parameter along with quotes
393  * @param ppsz_artist: Buffer to store artist name
394  * @param ppsz_title: Buffer to store title
395  * @param ppsz_album: Buffer to store album
396  * @param ppsz_genre: Buffer to store genre
397  * @param ppsz_year: Buffer to store year
398  * @param ppsz_cdnum: Buffer to store cdnum
399  * @param ppsz_comments: Buffer to store comments
400  */
401 static void ParseClipInfo( const char *psz_clipinfo, char **ppsz_artist, char **ppsz_title,
402                            char **ppsz_album, char **ppsz_genre, char **ppsz_year,
403                            char **ppsz_cdnum, char **ppsz_comments )
404 {
405     char *psz_option_next, *psz_option_start, *psz_param, *psz_value, *psz_suboption;
406     char *psz_temp_clipinfo = strdup( psz_clipinfo );
407     psz_option_start = strchr( psz_temp_clipinfo, '"' );
408     if( !psz_option_start )
409     {
410         free( psz_temp_clipinfo );
411         return;
412     }
413
414     psz_option_start++;
415     psz_option_next = psz_option_start;
416     while( 1 ) /* Process each sub option */
417     {
418         /* Get the sub option */
419         psz_option_start = psz_option_next;
420         psz_option_next = strchr( psz_option_start, '|' );
421         if( psz_option_next )
422             *psz_option_next = '\0';
423         else
424             psz_option_next = strchr( psz_option_start, '"' );
425         if( psz_option_next )
426             *psz_option_next = '\0';
427         else
428             psz_option_next = strchr( psz_option_start, '\0' );
429         if( psz_option_next == psz_option_start )
430             break;
431
432         psz_suboption = strdup( psz_option_start );
433         if( !psz_suboption )
434             break;
435
436         /* Parse out param and value */
437         psz_param = psz_suboption;
438         if( strchr( psz_suboption, '=' ) )
439         {
440             psz_value = strchr( psz_suboption, '=' ) + 1;
441             *( strchr( psz_suboption, '=' ) ) = '\0';
442         }
443         else
444             break;
445         /* Put into args */
446         if( !strcmp( psz_param, "artist name" ) )
447             *ppsz_artist = decode_URI_duplicate( psz_value );
448         else if( !strcmp( psz_param, "title" ) )
449             *ppsz_title = decode_URI_duplicate( psz_value );
450         else if( !strcmp( psz_param, "album name" ) )
451             *ppsz_album = decode_URI_duplicate( psz_value );
452         else if( !strcmp( psz_param, "genre" ) )
453             *ppsz_genre = decode_URI_duplicate( psz_value );
454         else if( !strcmp( psz_param, "year" ) )
455             *ppsz_year = decode_URI_duplicate( psz_value );
456         else if( !strcmp( psz_param, "cdnum" ) )
457             *ppsz_cdnum = decode_URI_duplicate( psz_value );
458         else if( !strcmp( psz_param, "comments" ) )
459             *ppsz_comments = decode_URI_duplicate( psz_value );
460
461         free( psz_suboption );
462         psz_option_next++;
463     }
464
465     free( psz_temp_clipinfo );
466 }