Commit 5e2e3995 authored by Srikanth Raju's avatar Srikanth Raju Committed by Jean-Baptiste Kempf

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: default avatarJean-Baptiste Kempf <jb@videolan.org>
parent c20cca81
...@@ -2,6 +2,7 @@ SOURCES_playlist = \ ...@@ -2,6 +2,7 @@ SOURCES_playlist = \
playlist.c \ playlist.c \
playlist.h \ playlist.h \
m3u.c \ m3u.c \
ram.c \
b4s.c \ b4s.c \
pls.c \ pls.c \
dvb.c \ dvb.c \
......
...@@ -70,6 +70,11 @@ vlc_module_begin () ...@@ -70,6 +70,11 @@ vlc_module_begin ()
add_shortcut( "m3u-open" ) add_shortcut( "m3u-open" )
set_capability( "demux", 10 ) set_capability( "demux", 10 )
set_callbacks( Import_M3U, Close_M3U ) 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 () add_submodule ()
set_description( N_("PLS playlist import") ) set_description( N_("PLS playlist import") )
add_shortcut( "pls-open" ) add_shortcut( "pls-open" )
......
...@@ -34,6 +34,9 @@ void Close_Native ( vlc_object_t * ); ...@@ -34,6 +34,9 @@ void Close_Native ( vlc_object_t * );
int Import_M3U ( vlc_object_t * ); int Import_M3U ( vlc_object_t * );
void Close_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 * ); int Import_PLS ( vlc_object_t * );
void Close_PLS ( vlc_object_t * ); void Close_PLS ( vlc_object_t * );
......
/*****************************************************************************
* ram.c : RAM playlist format import
*****************************************************************************
* Copyright (C) 2009 the VideoLAN team
* $Id$
*
* Authors: Srikanth Raju <srikiraju@gmail.com>
*
* 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."&copyright="&#169;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 <vlc_common.h>
#include <vlc_demux.h>
#include <vlc_charset.h>
#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&param2=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 );
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment