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