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>
53 #include <vlc_charset.h>
62 /*****************************************************************************
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 );
71 * Import_RAM: main import function
72 * @param p_this: this demux object
73 * @return VLC_SUCCESS if everything is okay
75 int Import_RAM( vlc_object_t *p_this )
77 demux_t *p_demux = (demux_t *)p_this;
78 const uint8_t *p_peek;
80 if(! demux_IsPathExtension( p_demux, ".ram" ) ||
81 demux_IsPathExtension( p_demux, ".rm" ) )
84 /* Many Real Media Files are misdetected */
85 if( stream_Peek( p_demux->s, &p_peek, 4 ) < 4 )
87 if( !memcmp( p_peek, ".ra", 3 ) || !memcmp( p_peek, ".RMF", 4 ) )
92 STANDARD_DEMUX_INIT_MSG( "found valid RAM playlist" );
93 p_demux->p_sys->psz_prefix = FindPrefix( p_demux );
99 * Frees up memory on module close
100 * @param p_this: this demux object
102 void Close_RAM( vlc_object_t *p_this )
104 demux_t *p_demux = (demux_t *)p_this;
105 free( p_demux->p_sys->psz_prefix );
106 free( p_demux->p_sys );
110 * Skips blanks in a given buffer
111 * @param s: input string
112 * @param i_strlen: length of the buffer
114 static const char *SkipBlanks( const char *s, size_t i_strlen )
116 while( i_strlen > 0 ) {
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
139 static int ParseTime( const char *s, size_t i_strlen)
141 // need to parse hour:minutes:sec.fraction string
144 const char *end = s + i_strlen;
145 // skip leading spaces if any
146 s = SkipBlanks(s, i_strlen);
149 while( (s < end) && isdigit((unsigned char)*s) )
151 int newval = val*10 + (*s - '0');
162 s = SkipBlanks(s, end-s);
166 s = SkipBlanks(s, end-s);
167 result = result * 60;
169 while( (s < end) && isdigit((unsigned char)*s) )
171 int newval = val*10 + (*s - '0');
182 s = SkipBlanks(s, end-s);
186 s = SkipBlanks(s, end-s);
187 result = result * 60;
189 while( (s < end) && isdigit((unsigned char)*s) )
191 int newval = val*10 + (*s - '0');
202 // TODO: one day, we may need to parse fraction for sub-second resolution
209 * Main demux callback function
210 * @param p_demux: this demux object
212 static int Demux( demux_t *p_demux )
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;
224 input_item_t *p_current_input = GetCurrentItem(p_demux);
226 input_item_node_t *p_subitems = input_item_node_Create( p_current_input );
228 psz_line = stream_ReadLine( p_demux->s );
231 char *psz_parse = psz_line;
233 /* Skip leading tabs and spaces */
234 while( *psz_parse == ' ' || *psz_parse == '\t' ||
235 *psz_parse == '\n' || *psz_parse == '\r' ) psz_parse++;
237 if( *psz_parse == '#' )
239 /* Ignore comments */
241 else if( *psz_parse )
243 char *psz_mrl, *psz_option_next, *psz_option;
244 char *psz_param, *psz_value;
246 /* Get the MRL from the file. Note that this might contain parameters of form ?param1=value1¶m2=value2 in a RAM file */
247 psz_mrl = ProcessMRL( psz_parse, p_demux->p_sys->psz_prefix );
250 if ( !psz_mrl ) goto error;
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 */
256 /* Remove options from MRL
257 because VLC can't get the file otherwise */
260 psz_option_next = psz_option;
261 while( 1 ) /* Process each option */
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 )
268 *psz_option_next = '\0';
272 psz_option_next = strchr( psz_option, '\0' );
273 /* Quit if options are over */
274 if( psz_option_next == psz_option )
277 /* Parse out param and value */
278 psz_param = psz_option;
279 psz_value = strchr( psz_option, '=' );
280 if( psz_value == NULL )
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" ) )
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 */
293 else if( !strcmp( psz_param, "author" ) )
295 psz_author = decode_URI_duplicate(psz_value);
296 EnsureUTF8( psz_author );
298 else if( !strcmp( psz_param, "start" )
299 && strncmp( psz_mrl, "rtsp", 4 ) /* Our rtsp-real or our real demuxer is wrong */ )
301 i_start = ParseTime( psz_value, strlen( psz_value ) );
305 if( asprintf( &temp, ":start-time=%d", i_start ) != -1 )
306 INSERT_ELEM( ppsz_options, i_options, i_options, temp );
309 else if( !strcmp( psz_param, "end" ) )
311 i_stop = ParseTime( psz_value, strlen( psz_value ) );
315 if( asprintf( &temp, ":stop-time=%d", i_stop ) != -1 )
316 INSERT_ELEM( ppsz_options, i_options, i_options, temp );
319 else if( !strcmp( psz_param, "title" ) )
321 psz_title = decode_URI_duplicate(psz_value);
322 EnsureUTF8( psz_title );
324 else if( !strcmp( psz_param, "copyright" ) )
326 psz_copyright = decode_URI_duplicate(psz_value);
327 EnsureUTF8( psz_copyright );
330 { /* TODO: insert option anyway? Currently ignores*/
331 /* INSERT_ELEM( ppsz_options, i_options, i_options, psz_option ); */
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 );
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 );
349 input_item_node_AppendItem( p_subitems, p_input );
350 vlc_gc_decref( p_input );
355 /* Fetch another line */
357 psz_line = stream_ReadLine( p_demux->s );
358 if( !psz_line ) b_cleanup = true;
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 );
375 i_parsed_duration = 0;
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 */
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
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 )
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 )
408 free( psz_temp_clipinfo );
413 psz_option_next = psz_option_start;
414 while( 1 ) /* Process each sub option */
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';
422 psz_option_next = strchr( psz_option_start, '"' );
423 if( psz_option_next )
424 *psz_option_next = '\0';
426 psz_option_next = strchr( psz_option_start, '\0' );
427 if( psz_option_next == psz_option_start )
430 psz_suboption = strdup( psz_option_start );
434 /* Parse out param and value */
435 psz_param = psz_suboption;
436 if( strchr( psz_suboption, '=' ) )
438 psz_value = strchr( psz_suboption, '=' ) + 1;
439 *( strchr( psz_suboption, '=' ) ) = '\0';
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 );
459 free( psz_suboption );
463 free( psz_temp_clipinfo );