1 /*****************************************************************************
2 * ram.c : RAM playlist format import
3 *****************************************************************************
4 * Copyright (C) 2009 the VideoLAN team
7 * Authors: Srikanth Raju <srikiraju@gmail.com>
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.
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.
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 *****************************************************************************/
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."©right="©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
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
43 /*****************************************************************************
45 *****************************************************************************/
50 #include <vlc_common.h>
51 #include <vlc_demux.h>
52 #include <vlc_charset.h>
63 /*****************************************************************************
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 );
74 * Import_RAM: main import function
75 * @param p_this: this demux object
76 * @return VLC_SUCCESS if everything is okay
78 int Import_RAM( vlc_object_t *p_this )
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" ) )
87 STANDARD_DEMUX_INIT_MSG( "found valid RAM playlist" );
88 p_demux->p_sys->psz_prefix = FindPrefix( p_demux );
94 * Frees up memory on module close
95 * @param p_this: this demux object
97 void Close_RAM( vlc_object_t *p_this )
99 demux_t *p_demux = (demux_t *)p_this;
100 free( p_demux->p_sys->psz_prefix );
101 free( p_demux->p_sys );
105 * Returns a UTF8 encoded version of the string
106 * @param str: input string
107 * @return pointer to UTF8 string
109 static inline char *MaybeFromLocaleDup (const char *str)
114 return IsUTF8 (str) ? strdup (str) : FromLocaleDup (str);
118 * Converts a string to UTF8 encoding
119 * @param str: input string
121 static inline void MaybeFromLocaleRep (char **str)
123 char *const orig_str = *str;
125 if ((orig_str != NULL) && !IsUTF8 (orig_str))
127 *str = FromLocaleDup (orig_str);
133 * Skips blanks in a given buffer
134 * @param s: input string
135 * @param i_strlen: length of the buffer
137 static char *SkipBlanks(char *s, size_t i_strlen )
139 while( i_strlen > 0 ) {
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
162 static int ParseTime(char *s, size_t i_strlen)
164 // need to parse hour:minutes:sec.fraction string
167 const char *end = s + i_strlen;
168 // skip leading spaces if any
169 s = SkipBlanks(s, i_strlen);
172 while( (s < end) && isdigit(*s) )
174 int newval = val*10 + (*s - '0');
185 s = SkipBlanks(s, end-s);
189 s = SkipBlanks(s, end-s);
190 result = result * 60;
192 while( (s < end) && isdigit(*s) )
194 int newval = val*10 + (*s - '0');
205 s = SkipBlanks(s, end-s);
209 s = SkipBlanks(s, end-s);
210 result = result * 60;
212 while( (s < end) && isdigit(*s) )
214 int newval = val*10 + (*s - '0');
225 // TODO: one day, we may need to parse fraction for sub-second resolution
232 * Main demux callback function
233 * @param p_demux: this demux object
235 static int Demux( demux_t *p_demux )
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;
250 psz_line = stream_ReadLine( p_demux->s );
253 char *psz_parse = psz_line;
255 /* Skip leading tabs and spaces */
256 while( *psz_parse == ' ' || *psz_parse == '\t' ||
257 *psz_parse == '\n' || *psz_parse == '\r' ) psz_parse++;
259 if( *psz_parse == '#' )
261 /* Ignore comments */
263 else if( *psz_parse )
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 )
269 /* Default filename as name for relative entries
270 TODO: Currently not used. Either remove or use */
271 psz_name = MaybeFromLocaleDup( psz_parse );
274 /* Get the MRL from the file. Note that this might contain parameters of form ?param1=value1¶m2=value2 in a RAM file */
275 psz_mrl = ProcessMRL( psz_parse, p_demux->p_sys->psz_prefix );
276 MaybeFromLocaleRep( &psz_mrl );
279 if ( !psz_mrl ) goto error;
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 )
287 psz_option_next = psz_option_start;
288 while( 1 ) /* Process each option */
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 )
295 *psz_option_next = '\0';
299 psz_option_next = strchr( psz_option_start, '\0' );
300 /* Quit if options are over */
301 if( psz_option_next == psz_option_start )
303 psz_option = MaybeFromLocaleDup( psz_option_start );
304 /* If this option is screwed up, try the next one */
308 /* Parse out param and value */
309 psz_param = psz_option;
310 if( strchr( psz_option, '=' ) )
312 psz_value = strchr( psz_option, '=' ) + 1;
313 *(strchr( psz_option, '=' )) = '\0';
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" ) )
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 */
326 else if( !strcmp( psz_param, "author" ) )
327 psz_author = strdup(psz_value);
328 else if( !strcmp( psz_param, "start" ) )
330 i_start = ParseTime( strdup( psz_value ),strlen( psz_value ) );
334 if( asprintf( &temp, ":start-time=%d", i_start ) == -1 )
337 INSERT_ELEM( ppsz_options, i_options, i_options, temp );
340 else if( !strcmp( psz_param, "end" ) )
342 i_stop = ParseTime( strdup( psz_value ), strlen( psz_value ) );
346 if( asprintf( &temp, ":stop-time=%d", i_stop ) == -1 )
349 INSERT_ELEM( ppsz_options, i_options, i_options, temp );
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);
357 { /* TODO: insert option anyway? Currently ignores*/
358 /* INSERT_ELEM( ppsz_options, i_options, i_options, psz_option ); */
364 *(strchr( psz_mrl, '?' )) = '\0'; /* Remove options from MRL because VLC can't get the file otherwise */
367 free( psz_temp_mrl );
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 );
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 );
382 input_item_AddSubItem( p_current_input, p_input );
383 vlc_gc_decref( p_input );
388 /* Fetch another line */
390 psz_line = stream_ReadLine( p_demux->s );
391 if( !psz_line ) b_cleanup = true;
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 );
409 i_parsed_duration = 0;
416 HANDLE_PLAY_AND_RELEASE;
417 var_Destroy( p_demux, "m3u-extvlcopt" );
418 return 0; /* Needed for correct operation of go back */
422 * @param p_demux: This object
424 * @param args: List of arguments
426 static int Control( demux_t *p_demux, int i_query, va_list args )
428 VLC_UNUSED(p_demux); VLC_UNUSED(i_query); VLC_UNUSED(args);
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
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 )
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 )
455 psz_option_next = psz_option_start;
456 while( 1 ) /* Process each sub option */
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';
464 psz_option_next = strchr( psz_option_start, '"' );
465 if( psz_option_next )
466 *psz_option_next = '\0';
468 psz_option_next = strchr( psz_option_start, '\0' );
469 if( psz_option_next == psz_option_start )
472 psz_suboption = MaybeFromLocaleDup( psz_option_start );
476 /* Parse out param and value */
477 psz_param = psz_suboption;
478 if( strchr( psz_suboption, '=' ) )
480 psz_value = strchr( psz_suboption, '=' ) + 1;
481 *( strchr( psz_suboption, '=' ) ) = '\0';
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 );
501 free( psz_suboption );
505 free( psz_temp_clipinfo );