From 5e2e3995097359e75684ff56873e1cbdc57ce1ad Mon Sep 17 00:00:00 2001 From: Srikanth Raju Date: Sat, 28 Mar 2009 02:46:44 +0530 Subject: [PATCH] Split RAM playlist support from m3u to its own demuxer and fix its support. Adds a RAM playlist demuxer to read .ram playlist files. Handles parameters author, copyright, clipinfo, start, end and title. Signed-off-by: Jean-Baptiste Kempf --- modules/demux/playlist/Modules.am | 1 + modules/demux/playlist/playlist.c | 5 + modules/demux/playlist/playlist.h | 3 + modules/demux/playlist/ram.c | 503 ++++++++++++++++++++++++++++++ 4 files changed, 512 insertions(+) create mode 100644 modules/demux/playlist/ram.c diff --git a/modules/demux/playlist/Modules.am b/modules/demux/playlist/Modules.am index 7f5895fcc5..6c1c377bba 100644 --- a/modules/demux/playlist/Modules.am +++ b/modules/demux/playlist/Modules.am @@ -2,6 +2,7 @@ SOURCES_playlist = \ playlist.c \ playlist.h \ m3u.c \ + ram.c \ b4s.c \ pls.c \ dvb.c \ diff --git a/modules/demux/playlist/playlist.c b/modules/demux/playlist/playlist.c index e74a6755eb..f50e968fe5 100644 --- a/modules/demux/playlist/playlist.c +++ b/modules/demux/playlist/playlist.c @@ -70,6 +70,11 @@ vlc_module_begin () add_shortcut( "m3u-open" ) set_capability( "demux", 10 ) set_callbacks( Import_M3U, Close_M3U ) + add_submodule () + set_description( N_("RAM playlist import") ) + add_shortcut( "ram-open" ) + set_capability( "demux", 10 ) + set_callbacks( Import_RAM, Close_RAM ) add_submodule () set_description( N_("PLS playlist import") ) add_shortcut( "pls-open" ) diff --git a/modules/demux/playlist/playlist.h b/modules/demux/playlist/playlist.h index bfa0666db7..13803f6bb5 100644 --- a/modules/demux/playlist/playlist.h +++ b/modules/demux/playlist/playlist.h @@ -34,6 +34,9 @@ void Close_Native ( vlc_object_t * ); int Import_M3U ( vlc_object_t * ); void Close_M3U ( vlc_object_t * ); +int Import_RAM ( vlc_object_t * ); +void Close_RAM ( vlc_object_t * ); + int Import_PLS ( vlc_object_t * ); void Close_PLS ( vlc_object_t * ); diff --git a/modules/demux/playlist/ram.c b/modules/demux/playlist/ram.c new file mode 100644 index 0000000000..8b2f63cb6a --- /dev/null +++ b/modules/demux/playlist/ram.c @@ -0,0 +1,503 @@ +/***************************************************************************** + * ram.c : RAM playlist format import + ***************************************************************************** + * Copyright (C) 2009 the VideoLAN team + * $Id$ + * + * Authors: Srikanth Raju + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/* +An example: +rtsp://helixserver.example.com/video1.rm?rpcontextheight=250 +&rpcontextwidth=280&rpcontexturl="http://www.example.com/relatedinfo1.html" +rtsp://helixserver.example.com/video2.rm?rpurl="http://www.example.com/index.html" +rtsp://helixserver.example.com/sample1.smil?screensize=full +rtsp://helixserver.example.com/audio1.rm?start=55&end=1:25 +rtsp://helixserver.example.com/introvid.rm?title="Introduction to Streaming Media +Production"&author="RealNetworks, Inc."©right="©2001, RealNetworks, Inc." +rtsp://helixserver.example.com/song1.rm?clipinfo="title=Artist of the Year|artist name=Your Name +Here|album name=My Debut|genre=Rock|copyright=2001|year=2001|comments=This one really +knows how to rock!" + +See also: +http://service.real.com/help/library/guides/realone/IntroGuide/HTML/htmfiles/ramsum.htm +http://service.real.com/help/library/guides/realone/IntroGuide/HTML/htmfiles/ramfile.htm +*/ + + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "playlist.h" + +struct demux_sys_t +{ + char *psz_prefix; +}; + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static int Demux( demux_t *p_demux); +static int Control( demux_t *p_demux, int i_query, va_list args ); +static bool ContainsURL( demux_t *p_demux ); +static void ParseClipInfo( char * psz_clipinfo, char **ppsz_artist, char **ppsz_title, + char **ppsz_album, char **ppsz_genre, char **ppsz_year, + char **ppsz_cdnum, char **ppsz_comments ); + +/** + * Import_RAM: main import function + * @param p_this: this demux object + * @return VLC_SUCCESS if everything is okay + */ +int Import_RAM( vlc_object_t *p_this ) +{ + demux_t *p_demux = (demux_t *)p_this; + const uint8_t *p_peek; + CHECK_PEEK( p_peek, 8 ); + if(! demux_IsPathExtension( p_demux, ".ram" ) ) + return VLC_EGENERIC; + + STANDARD_DEMUX_INIT_MSG( "found valid RAM playlist" ); + p_demux->p_sys->psz_prefix = FindPrefix( p_demux ); + + return VLC_SUCCESS; +} + +/** + * Frees up memory on module close + * @param p_this: this demux object + */ +void Close_RAM( vlc_object_t *p_this ) +{ + demux_t *p_demux = (demux_t *)p_this; + free( p_demux->p_sys->psz_prefix ); + free( p_demux->p_sys ); +} + +/** + * Returns a UTF8 encoded version of the string + * @param str: input string + * @return pointer to UTF8 string + */ +static inline char *MaybeFromLocaleDup (const char *str) +{ + if (str == NULL) + return NULL; + + return IsUTF8 (str) ? strdup (str) : FromLocaleDup (str); +} + +/** + * Converts a string to UTF8 encoding + * @param str: input string + */ +static inline void MaybeFromLocaleRep (char **str) +{ + char *const orig_str = *str; + + if ((orig_str != NULL) && !IsUTF8 (orig_str)) + { + *str = FromLocaleDup (orig_str); + free (orig_str); + } +} + +/** + * Skips blanks in a given buffer + * @param s: input string + * @param i_strlen: length of the buffer + */ +static char *SkipBlanks(char *s, size_t i_strlen ) +{ + while( i_strlen > 0 ) { + switch( *s ) + { + case ' ': + case '\t': + case '\r': + case '\n': + --i_strlen; + ++s; + break; + default: + i_strlen = 0; + } + } + return s; +} + +/** + * Converts a time of format hour:minutes:sec.fraction to seconds + * @param s: input string + * @param i_strlen: length of the buffer + * @return time in seconds + */ +static int ParseTime(char *s, size_t i_strlen) +{ + // need to parse hour:minutes:sec.fraction string + int result = 0; + int val; + const char *end = s + i_strlen; + // skip leading spaces if any + s = SkipBlanks(s, i_strlen); + + val = 0; + while( (s < end) && isdigit(*s) ) + { + int newval = val*10 + (*s - '0'); + if( newval < val ) + { + // overflow + val = 0; + break; + } + val = newval; + ++s; + } + result = val; + s = SkipBlanks(s, end-s); + if( *s == ':' ) + { + ++s; + s = SkipBlanks(s, end-s); + result = result * 60; + val = 0; + while( (s < end) && isdigit(*s) ) + { + int newval = val*10 + (*s - '0'); + if( newval < val ) + { + // overflow + val = 0; + break; + } + val = newval; + ++s; + } + result += val; + s = SkipBlanks(s, end-s); + if( *s == ':' ) + { + ++s; + s = SkipBlanks(s, end-s); + result = result * 60; + val = 0; + while( (s < end) && isdigit(*s) ) + { + int newval = val*10 + (*s - '0'); + if( newval < val ) + { + // overflow + val = 0; + break; + } + val = newval; + ++s; + } + result += val; + // TODO: one day, we may need to parse fraction for sub-second resolution + } + } + return result; +} + +/** + * Main demux callback function + * @param p_demux: this demux object + */ +static int Demux( demux_t *p_demux ) +{ + char *psz_line; + char *psz_name = NULL; + char *psz_artist = NULL, *psz_album = NULL, *psz_genre = NULL, *psz_year = NULL; + char *psz_author = NULL, *psz_title = NULL, *psz_copyright = NULL, *psz_cdnum = NULL, *psz_comments = NULL; + int i_parsed_duration = 0; + mtime_t i_duration = -1; + const char **ppsz_options = NULL; + int i_options = 0, i_start = 0, i_stop = 0; + bool b_cleanup = false; + input_item_t *p_input; + + INIT_PLAYLIST_STUFF; + + psz_line = stream_ReadLine( p_demux->s ); + while( psz_line ) + { + char *psz_parse = psz_line; + + /* Skip leading tabs and spaces */ + while( *psz_parse == ' ' || *psz_parse == '\t' || + *psz_parse == '\n' || *psz_parse == '\r' ) psz_parse++; + + if( *psz_parse == '#' ) + { + /* Ignore comments */ + } + else if( *psz_parse ) + { + char *psz_mrl, *psz_option_start, *psz_option_next, *psz_temp_mrl, *psz_option; + char *psz_param, *psz_value; + if( !psz_name || !*psz_name ) + { + /* Default filename as name for relative entries + TODO: Currently not used. Either remove or use */ + psz_name = MaybeFromLocaleDup( psz_parse ); + } + + /* Get the MRL from the file. Note that this might contain parameters of form ?param1=value1¶m2=value2 in a RAM file */ + psz_mrl = ProcessMRL( psz_parse, p_demux->p_sys->psz_prefix ); + MaybeFromLocaleRep( &psz_mrl ); + + b_cleanup = true; + if ( !psz_mrl ) goto error; + + /* We have the MRL, now we have to check for options and parse them from MRL */ + psz_temp_mrl = strdup( psz_mrl ); + psz_option_start = strchr( psz_temp_mrl, '?' ); /* Look for start of options */ + if( psz_option_start ) + { + psz_option_start++; + psz_option_next = psz_option_start; + while( 1 ) /* Process each option */ + { + /* Look for end of first option which maybe a & or \0 */ + psz_option_start = psz_option_next; + psz_option_next = strchr( psz_option_start, '&' ); + if( psz_option_next ) + { + *psz_option_next = '\0'; + psz_option_next++; + } + else + psz_option_next = strchr( psz_option_start, '\0' ); + /* Quit if options are over */ + if( psz_option_next == psz_option_start ) + break; + psz_option = MaybeFromLocaleDup( psz_option_start ); + /* If this option is screwed up, try the next one */ + if( !psz_option ) + continue; + + /* Parse out param and value */ + psz_param = psz_option; + if( strchr( psz_option, '=' ) ) + { + psz_value = strchr( psz_option, '=' ) + 1; + *(strchr( psz_option, '=' )) = '\0'; + } + else + break; + + /* Take action based on parameter value in the below if else structure */ + /* TODO: Remove any quotes surrounding values if required */ + if( !strcmp( psz_param, "clipinfo" ) ) + { + ParseClipInfo( psz_value, &psz_artist, &psz_title, + &psz_album, &psz_genre, &psz_year, + &psz_cdnum, &psz_comments ); /* clipinfo has various sub parameters, which is parsed by this function */ + } + else if( !strcmp( psz_param, "author" ) ) + psz_author = strdup(psz_value); + else if( !strcmp( psz_param, "start" ) ) + { + i_start = ParseTime( strdup( psz_value ),strlen( psz_value ) ); + char * temp = NULL; + if( i_start ) + { + if( asprintf( &temp, ":start-time=%d", i_start ) == -1 ) + *(temp) = NULL; + if( temp && *temp ) + INSERT_ELEM( ppsz_options, i_options, i_options, temp ); + } + } + else if( !strcmp( psz_param, "end" ) ) + { + i_stop = ParseTime( strdup( psz_value ), strlen( psz_value ) ); + char * temp = NULL; + if( i_stop ) + { + if( asprintf( &temp, ":stop-time=%d", i_stop ) == -1 ) + *(temp) = NULL; + if( temp && *temp ) + INSERT_ELEM( ppsz_options, i_options, i_options, temp ); + } + } + else if( !strcmp( psz_param, "title" ) ) + psz_title = strdup(psz_value); + else if( !strcmp( psz_param, "copyright" ) ) + psz_copyright = strdup(psz_value); + else + { /* TODO: insert option anyway? Currently ignores*/ + /* INSERT_ELEM( ppsz_options, i_options, i_options, psz_option ); */ + } + + free( psz_option ); + } + + *(strchr( psz_mrl, '?' )) = '\0'; /* Remove options from MRL because VLC can't get the file otherwise */ + } + + free( psz_temp_mrl ); + + /* Create the input item and pump in all the options into playlist item */ + p_input = input_item_NewExt( p_demux, psz_mrl, psz_title, i_options, ppsz_options, 0, i_duration ); + + if( psz_artist && *psz_artist ) input_item_SetArtist( p_input, psz_artist ); + if( psz_author && *psz_author ) input_item_SetPublisher( p_input, psz_author ); + if( psz_title && *psz_title ) input_item_SetTitle( p_input, psz_title ); + if( psz_copyright && *psz_copyright ) input_item_SetCopyright( p_input, psz_copyright ); + if( psz_album && *psz_album ) input_item_SetAlbum( p_input, psz_album ); + if( psz_genre && *psz_genre ) input_item_SetGenre( p_input, psz_genre ); + if( psz_year && *psz_year ) input_item_SetDate( p_input, psz_copyright ); + if( psz_cdnum && *psz_cdnum ) input_item_SetTrackNum( p_input, psz_cdnum ); + if( psz_comments && *psz_comments ) input_item_SetDescription( p_input, psz_comments ); + + input_item_AddSubItem( p_current_input, p_input ); + vlc_gc_decref( p_input ); + free( psz_mrl ); + } + + error: + /* Fetch another line */ + free( psz_line ); + psz_line = stream_ReadLine( p_demux->s ); + if( !psz_line ) b_cleanup = true; + + if( b_cleanup ) + { + /* Cleanup state */ + while( i_options-- ) free( (char*)ppsz_options[i_options] ); + FREENULL( ppsz_options ); + FREENULL( psz_name ); + FREENULL( psz_artist ); + FREENULL( psz_title ); + FREENULL( psz_author ); + FREENULL( psz_copyright ); + FREENULL( psz_album ); + FREENULL( psz_genre ); + FREENULL( psz_year ); + FREENULL( psz_cdnum ); + FREENULL( psz_comments ); + i_options = 0; + i_parsed_duration = 0; + i_duration = -1; + i_start = 0; + i_stop = 0; + b_cleanup = false; + } + } + HANDLE_PLAY_AND_RELEASE; + var_Destroy( p_demux, "m3u-extvlcopt" ); + return 0; /* Needed for correct operation of go back */ +} + +/** + * @param p_demux: This object + * @param i_query: + * @param args: List of arguments + */ +static int Control( demux_t *p_demux, int i_query, va_list args ) +{ + VLC_UNUSED(p_demux); VLC_UNUSED(i_query); VLC_UNUSED(args); + return VLC_EGENERIC; +} + +/** + * Parses clipinfo parameter + * @param psz_clipinfo: string containing the clipinfo parameter along with quotes + * @param ppsz_artist: Buffer to store artist name + * @param ppsz_title: Buffer to store title + * @param ppsz_album: Buffer to store album + * @param ppsz_genre: Buffer to store genre + * @param ppsz_year: Buffer to store year + * @param ppsz_cdnum: Buffer to store cdnum + * @param ppsz_comments: Buffer to store comments + */ +static void ParseClipInfo( char *psz_clipinfo, char **ppsz_artist, char **ppsz_title, + char **ppsz_album, char **ppsz_genre, char **ppsz_year, + char **ppsz_cdnum, char **ppsz_comments ) +{ + char *psz_option_next, *psz_option_start, *psz_param, *psz_value, *psz_suboption; + char *psz_temp_clipinfo = strdup( psz_clipinfo ); + psz_option_start = psz_clipinfo; + psz_option_start = strchr( psz_temp_clipinfo, '"' ); + if( !psz_option_start ) + return; + + psz_option_start++; + psz_option_next = psz_option_start; + while( 1 ) /* Process each sub option */ + { + /* Get the sub option */ + psz_option_start = psz_option_next; + psz_option_next = strchr( psz_option_start, '|' ); + if( psz_option_next ) + *psz_option_next = '\0'; + else + psz_option_next = strchr( psz_option_start, '"' ); + if( psz_option_next ) + *psz_option_next = '\0'; + else + psz_option_next = strchr( psz_option_start, '\0' ); + if( psz_option_next == psz_option_start ) + break; + + psz_suboption = MaybeFromLocaleDup( psz_option_start ); + if( !psz_suboption ) + break; + + /* Parse out param and value */ + psz_param = psz_suboption; + if( strchr( psz_suboption, '=' ) ) + { + psz_value = strchr( psz_suboption, '=' ) + 1; + *( strchr( psz_suboption, '=' ) ) = '\0'; + } + else + break; + /* Put into args */ + if( !strcmp( psz_param, "artist name" ) ) + *ppsz_artist = strdup( psz_value ); + else if( !strcmp( psz_param, "title" ) ) + *ppsz_title = strdup( psz_value ); + else if( !strcmp( psz_param, "album name" ) ) + *ppsz_album = strdup( psz_value ); + else if( !strcmp( psz_param, "genre" ) ) + *ppsz_genre = strdup( psz_value ); + else if( !strcmp( psz_param, "year" ) ) + *ppsz_year = strdup( psz_value ); + else if( !strcmp( psz_param, "cdnum" ) ) + *ppsz_cdnum = strdup( psz_value ); + else if( !strcmp( psz_param, "comments" ) ) + *ppsz_comments = strdup( psz_value ); + + free( psz_suboption ); + psz_option_next++; + } + + free( psz_temp_clipinfo ); +} -- 2.39.2