asx: fix mimetype and stream Peek
[vlc.git] / modules / demux / playlist / ram.c
blob1204d3f3ba7ece0c3523553eefca5770bb0aeb60
1 /*****************************************************************************
2 * ram.c : RAM playlist format import
3 *****************************************************************************
4 * Copyright (C) 2009 VLC authors and VideoLAN
5 * $Id$
7 * Authors: Srikanth Raju <srikiraju@gmail.com>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
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!"
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
43 /*****************************************************************************
44 * Preamble
45 *****************************************************************************/
46 #ifdef HAVE_CONFIG_H
47 # include "config.h"
48 #endif
50 #include <ctype.h>
52 #include <vlc_common.h>
53 #include <vlc_access.h>
54 #include <vlc_url.h>
55 #include <vlc_charset.h>
57 #include "playlist.h"
59 /*****************************************************************************
60 * Local prototypes
61 *****************************************************************************/
62 static int ReadDir( stream_t *, input_item_node_t * );
63 static void ParseClipInfo( const char * psz_clipinfo, char **ppsz_artist, char **ppsz_title,
64 char **ppsz_album, char **ppsz_genre, char **ppsz_year,
65 char **ppsz_cdnum, char **ppsz_comments );
67 /**
68 * Import_RAM: main import function
69 * @param p_this: this demux object
70 * @return VLC_SUCCESS if everything is okay
72 int Import_RAM( vlc_object_t *p_this )
74 stream_t *p_demux = (stream_t *)p_this;
75 const uint8_t *p_peek;
77 CHECK_FILE(p_demux);
78 if( !stream_HasExtension( p_demux, ".ram" )
79 && !stream_HasExtension( p_demux, ".rm" ) )
80 return VLC_EGENERIC;
82 /* Many Real Media Files are misdetected */
83 if( vlc_stream_Peek( p_demux->s, &p_peek, 4 ) < 4 )
84 return VLC_EGENERIC;
85 if( !memcmp( p_peek, ".ra", 3 ) || !memcmp( p_peek, ".RMF", 4 ) )
87 return VLC_EGENERIC;
90 msg_Dbg( p_demux, "found valid RAM playlist" );
91 p_demux->pf_readdir = ReadDir;
92 p_demux->pf_control = access_vaDirectoryControlHelper;
94 return VLC_SUCCESS;
97 /**
98 * Skips blanks in a given buffer
99 * @param s: input string
100 * @param i_strlen: length of the buffer
102 static const char *SkipBlanks( const char *s, size_t i_strlen )
104 while( i_strlen > 0 ) {
105 switch( *s )
107 case ' ':
108 case '\t':
109 case '\r':
110 case '\n':
111 --i_strlen;
112 ++s;
113 break;
114 default:
115 i_strlen = 0;
118 return s;
122 * Converts a time of format hour:minutes:sec.fraction to seconds
123 * @param s: input string
124 * @param i_strlen: length of the buffer
125 * @return time in seconds
127 static int ParseTime( const char *s, size_t i_strlen)
129 // need to parse hour:minutes:sec.fraction string
130 int result = 0;
131 int val;
132 const char *end = s + i_strlen;
133 // skip leading spaces if any
134 s = SkipBlanks(s, i_strlen);
136 val = 0;
137 while( (s < end) && isdigit((unsigned char)*s) )
139 int newval = val*10 + (*s - '0');
140 if( newval < val )
142 // overflow
143 val = 0;
144 break;
146 val = newval;
147 ++s;
149 result = val;
150 s = SkipBlanks(s, end-s);
151 if( *s == ':' )
153 ++s;
154 s = SkipBlanks(s, end-s);
155 result = result * 60;
156 val = 0;
157 while( (s < end) && isdigit((unsigned char)*s) )
159 int newval = val*10 + (*s - '0');
160 if( newval < val )
162 // overflow
163 val = 0;
164 break;
166 val = newval;
167 ++s;
169 result += val;
170 s = SkipBlanks(s, end-s);
171 if( *s == ':' )
173 ++s;
174 s = SkipBlanks(s, end-s);
175 result = result * 60;
176 val = 0;
177 while( (s < end) && isdigit((unsigned char)*s) )
179 int newval = val*10 + (*s - '0');
180 if( newval < val )
182 // overflow
183 val = 0;
184 break;
186 val = newval;
187 ++s;
189 result += val;
190 // TODO: one day, we may need to parse fraction for sub-second resolution
193 return result;
196 static int ReadDir( stream_t *p_demux, input_item_node_t *p_subitems )
198 const char *psz_prefix = p_demux->psz_url;
199 if( unlikely(psz_prefix == NULL) )
200 return VLC_SUCCESS;
202 char *psz_line;
203 char *psz_artist = NULL, *psz_album = NULL, *psz_genre = NULL, *psz_year = NULL;
204 char *psz_author = NULL, *psz_title = NULL, *psz_copyright = NULL, *psz_cdnum = NULL, *psz_comments = NULL;
205 mtime_t i_duration = -1;
206 const char **ppsz_options = NULL;
207 int i_options = 0, i_start = 0, i_stop = 0;
208 bool b_cleanup = false;
209 input_item_t *p_input;
211 psz_line = vlc_stream_ReadLine( p_demux->s );
212 while( psz_line )
214 char *psz_parse = psz_line;
216 /* Skip leading tabs and spaces */
217 while( *psz_parse == ' ' || *psz_parse == '\t' ||
218 *psz_parse == '\n' || *psz_parse == '\r' ) psz_parse++;
220 if( *psz_parse == '#' )
222 /* Ignore comments */
224 else if( *psz_parse )
226 char *psz_mrl, *psz_option_next, *psz_option;
227 char *psz_param, *psz_value;
229 /* Get the MRL from the file. Note that this might contain parameters of form ?param1=value1&param2=value2 in a RAM file */
230 psz_mrl = ProcessMRL( psz_parse, psz_prefix );
232 b_cleanup = true;
233 if ( !psz_mrl ) goto error;
235 /* We have the MRL, now we have to check for options and parse them from MRL */
236 psz_option = strchr( psz_mrl, '?' ); /* Look for start of options */
237 if( psz_option )
239 /* Remove options from MRL
240 because VLC can't get the file otherwise */
241 *psz_option = '\0';
242 psz_option++;
243 psz_option_next = psz_option;
244 while( 1 ) /* Process each option */
246 /* Look for end of first option which maybe a & or \0 */
247 psz_option = psz_option_next;
248 psz_option_next = strchr( psz_option, '&' );
249 if( psz_option_next )
251 *psz_option_next = '\0';
252 psz_option_next++;
254 else
255 psz_option_next = strchr( psz_option, '\0' );
256 /* Quit if options are over */
257 if( psz_option_next == psz_option )
258 break;
260 /* Parse out param and value */
261 psz_param = psz_option;
262 psz_value = strchr( psz_option, '=' );
263 if( psz_value == NULL )
264 break;
265 *psz_value = '\0';
266 psz_value++;
268 /* Take action based on parameter value in the below if else structure */
269 /* TODO: Remove any quotes surrounding values if required */
270 if( !strcmp( psz_param, "clipinfo" ) )
272 ParseClipInfo( psz_value, &psz_artist, &psz_title,
273 &psz_album, &psz_genre, &psz_year,
274 &psz_cdnum, &psz_comments ); /* clipinfo has various sub parameters, which is parsed by this function */
276 else if( !strcmp( psz_param, "author" ) )
278 psz_author = vlc_uri_decode_duplicate(psz_value);
279 EnsureUTF8( psz_author );
281 else if( !strcmp( psz_param, "start" )
282 && strncmp( psz_mrl, "rtsp", 4 ) /* Our rtsp-real or our real demuxer is wrong */ )
284 i_start = ParseTime( psz_value, strlen( psz_value ) );
285 char *temp;
286 if( i_start )
288 if( asprintf( &temp, ":start-time=%d", i_start ) != -1 )
289 TAB_APPEND( i_options, ppsz_options, temp );
292 else if( !strcmp( psz_param, "end" ) )
294 i_stop = ParseTime( psz_value, strlen( psz_value ) );
295 char *temp;
296 if( i_stop )
298 if( asprintf( &temp, ":stop-time=%d", i_stop ) != -1 )
299 TAB_APPEND( i_options, ppsz_options, temp );
302 else if( !strcmp( psz_param, "title" ) )
304 free( psz_title );
305 psz_title = vlc_uri_decode_duplicate(psz_value);
306 EnsureUTF8( psz_title );
308 else if( !strcmp( psz_param, "copyright" ) )
310 psz_copyright = vlc_uri_decode_duplicate(psz_value);
311 EnsureUTF8( psz_copyright );
313 else
314 { /* TODO: insert option anyway? Currently ignores*/
315 //TAB_APPEND( i_options, ppsz_options, psz_option );
320 /* Create the input item and pump in all the options into playlist item */
321 p_input = input_item_NewExt( psz_mrl, psz_title, i_duration,
322 ITEM_TYPE_UNKNOWN, ITEM_NET_UNKNOWN );
323 if( !p_input )
325 free( psz_mrl );
326 goto error;
328 if( ppsz_options )
329 input_item_AddOptions( p_input, i_options, ppsz_options, 0 );
331 if( !EMPTY_STR( psz_artist ) ) input_item_SetArtist( p_input, psz_artist );
332 if( !EMPTY_STR( psz_author ) ) input_item_SetPublisher( p_input, psz_author );
333 if( !EMPTY_STR( psz_title ) ) input_item_SetTitle( p_input, psz_title );
334 if( !EMPTY_STR( psz_copyright ) ) input_item_SetCopyright( p_input, psz_copyright );
335 if( !EMPTY_STR( psz_album ) ) input_item_SetAlbum( p_input, psz_album );
336 if( !EMPTY_STR( psz_genre ) ) input_item_SetGenre( p_input, psz_genre );
337 if( !EMPTY_STR( psz_year ) ) input_item_SetDate( p_input, psz_year );
338 if( !EMPTY_STR( psz_cdnum ) ) input_item_SetTrackNum( p_input, psz_cdnum );
339 if( !EMPTY_STR( psz_comments ) ) input_item_SetDescription( p_input, psz_comments );
341 input_item_node_AppendItem( p_subitems, p_input );
342 input_item_Release( p_input );
343 free( psz_mrl );
346 error:
347 /* Fetch another line */
348 free( psz_line );
349 psz_line = vlc_stream_ReadLine( p_demux->s );
350 if( !psz_line ) b_cleanup = true;
352 if( b_cleanup )
354 /* Cleanup state */
355 while( i_options-- ) free( (char*)ppsz_options[i_options] );
356 FREENULL( ppsz_options );
357 FREENULL( psz_artist );
358 FREENULL( psz_title );
359 FREENULL( psz_author );
360 FREENULL( psz_copyright );
361 FREENULL( psz_album );
362 FREENULL( psz_genre );
363 FREENULL( psz_year );
364 FREENULL( psz_cdnum );
365 FREENULL( psz_comments );
366 i_options = 0;
367 i_duration = -1;
368 i_start = 0;
369 i_stop = 0;
370 b_cleanup = false;
373 return VLC_SUCCESS;
377 * Parses clipinfo parameter
378 * @param psz_clipinfo: string containing the clipinfo parameter along with quotes
379 * @param ppsz_artist: Buffer to store artist name
380 * @param ppsz_title: Buffer to store title
381 * @param ppsz_album: Buffer to store album
382 * @param ppsz_genre: Buffer to store genre
383 * @param ppsz_year: Buffer to store year
384 * @param ppsz_cdnum: Buffer to store cdnum
385 * @param ppsz_comments: Buffer to store comments
387 static void ParseClipInfo( const char *psz_clipinfo, char **ppsz_artist, char **ppsz_title,
388 char **ppsz_album, char **ppsz_genre, char **ppsz_year,
389 char **ppsz_cdnum, char **ppsz_comments )
391 char *psz_option_next, *psz_option_start, *psz_param, *psz_value, *psz_suboption;
392 char *psz_temp_clipinfo = strdup( psz_clipinfo );
393 psz_option_start = strchr( psz_temp_clipinfo, '"' );
394 if( !psz_option_start )
396 free( psz_temp_clipinfo );
397 return;
400 psz_option_start++;
401 psz_option_next = psz_option_start;
402 while( 1 ) /* Process each sub option */
404 /* Get the sub option */
405 psz_option_start = psz_option_next;
406 psz_option_next = strchr( psz_option_start, '|' );
407 if( psz_option_next )
408 *psz_option_next = '\0';
409 else
410 psz_option_next = strchr( psz_option_start, '"' );
411 if( psz_option_next )
412 *psz_option_next = '\0';
413 else
414 psz_option_next = strchr( psz_option_start, '\0' );
415 if( psz_option_next == psz_option_start )
416 break;
418 psz_suboption = strdup( psz_option_start );
419 if( !psz_suboption )
420 break;
422 /* Parse out param and value */
423 psz_param = psz_suboption;
424 if( strchr( psz_suboption, '=' ) )
426 psz_value = strchr( psz_suboption, '=' ) + 1;
427 *( strchr( psz_suboption, '=' ) ) = '\0';
429 else
431 free( psz_suboption );
432 break;
434 /* Put into args */
435 if( !strcmp( psz_param, "artist name" ) )
436 *ppsz_artist = vlc_uri_decode_duplicate( psz_value );
437 else if( !strcmp( psz_param, "title" ) )
438 *ppsz_title = vlc_uri_decode_duplicate( psz_value );
439 else if( !strcmp( psz_param, "album name" ) )
440 *ppsz_album = vlc_uri_decode_duplicate( psz_value );
441 else if( !strcmp( psz_param, "genre" ) )
442 *ppsz_genre = vlc_uri_decode_duplicate( psz_value );
443 else if( !strcmp( psz_param, "year" ) )
444 *ppsz_year = vlc_uri_decode_duplicate( psz_value );
445 else if( !strcmp( psz_param, "cdnum" ) )
446 *ppsz_cdnum = vlc_uri_decode_duplicate( psz_value );
447 else if( !strcmp( psz_param, "comments" ) )
448 *ppsz_comments = vlc_uri_decode_duplicate( psz_value );
450 free( psz_suboption );
451 psz_option_next++;
454 free( psz_temp_clipinfo );