/*****************************************************************************
* Preamble
*****************************************************************************/
-#include <string.h>
-#include <vlc/vlc.h>
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
#include <vlc_sout.h>
#include <vlc_block.h>
"website. Requires the bitrate information specified for " \
"shoutcast. Requires Ogg streaming for icecast." )
-vlc_module_begin();
- set_description( _("IceCAST output") );
- set_shortname( "Shoutcast" );
- set_capability( "sout access", 50 );
- set_category( CAT_SOUT );
- set_subcategory( SUBCAT_SOUT_ACO );
- add_shortcut( "shout" );
+vlc_module_begin ()
+ set_description( N_("IceCAST output") )
+ set_shortname( "Shoutcast" )
+ set_capability( "sout access", 0 )
+ set_category( CAT_SOUT )
+ set_subcategory( SUBCAT_SOUT_ACO )
+ add_shortcut( "shout" )
add_string( SOUT_CFG_PREFIX "name", "VLC media player - Live stream", NULL,
- NAME_TEXT, NAME_LONGTEXT, VLC_FALSE );
+ NAME_TEXT, NAME_LONGTEXT, false )
add_string( SOUT_CFG_PREFIX "description",
"Live stream from VLC media player", NULL,
- DESCRIPTION_TEXT, DESCRIPTION_LONGTEXT, VLC_FALSE );
- add_bool( SOUT_CFG_PREFIX "mp3", VLC_FALSE, NULL,
- MP3_TEXT, MP3_LONGTEXT, VLC_TRUE );
+ DESCRIPTION_TEXT, DESCRIPTION_LONGTEXT, false )
+ add_bool( SOUT_CFG_PREFIX "mp3", false, NULL,
+ MP3_TEXT, MP3_LONGTEXT, true )
add_string( SOUT_CFG_PREFIX "genre", "Alternative", NULL,
- GENRE_TEXT, GENRE_LONGTEXT, VLC_FALSE );
+ GENRE_TEXT, GENRE_LONGTEXT, false )
add_string( SOUT_CFG_PREFIX "url", "http://www.videolan.org/vlc", NULL,
- URL_TEXT, URL_LONGTEXT, VLC_FALSE );
+ URL_TEXT, URL_LONGTEXT, false )
add_string( SOUT_CFG_PREFIX "bitrate", "", NULL,
- BITRATE_TEXT, BITRATE_LONGTEXT, VLC_FALSE );
+ BITRATE_TEXT, BITRATE_LONGTEXT, false )
add_string( SOUT_CFG_PREFIX "samplerate", "", NULL,
- SAMPLERATE_TEXT, SAMPLERATE_LONGTEXT, VLC_FALSE );
+ SAMPLERATE_TEXT, SAMPLERATE_LONGTEXT, false )
add_string( SOUT_CFG_PREFIX "channels", "", NULL,
- CHANNELS_TEXT, CHANNELS_LONGTEXT, VLC_FALSE );
+ CHANNELS_TEXT, CHANNELS_LONGTEXT, false )
add_string( SOUT_CFG_PREFIX "quality", "", NULL,
- QUALITY_TEXT, QUALITY_LONGTEXT, VLC_FALSE );
- add_bool( SOUT_CFG_PREFIX "public", VLC_FALSE, NULL,
- PUBLIC_TEXT, PUBLIC_LONGTEXT, VLC_TRUE );
- set_callbacks( Open, Close );
-vlc_module_end();
+ QUALITY_TEXT, QUALITY_LONGTEXT, false )
+ add_bool( SOUT_CFG_PREFIX "public", false, NULL,
+ PUBLIC_TEXT, PUBLIC_LONGTEXT, true )
+ set_callbacks( Open, Close )
+vlc_module_end ()
/*****************************************************************************
* Exported prototypes
*****************************************************************************/
-static const char *ppsz_sout_options[] = {
+static const char *const ppsz_sout_options[] = {
"name", "description", "mp3", "genre", "url", "bitrate", "samplerate",
"channels", "quality", "public", NULL
};
/*****************************************************************************
* Exported prototypes
*****************************************************************************/
-static int Write( sout_access_out_t *, block_t * );
+static ssize_t Write( sout_access_out_t *, block_t * );
static int Seek ( sout_access_out_t *, off_t );
-static int Read ( sout_access_out_t *, block_t * );
+static int Control( sout_access_out_t *, int, va_list );
struct sout_access_out_sys_t
{
shout_t *p_shout;
long i_ret;
unsigned int i_port;
- vlc_value_t val;
-
- char *psz_accessname = NULL;
- char *psz_parser = NULL;
- char *psz_user = NULL;
- char *psz_pass = NULL;
- char *psz_host = NULL;
- char *psz_mount = NULL;
- char *psz_name = NULL;
- char *psz_description = NULL;
- char *tmp_port = NULL;
- char *psz_genre = NULL;
- char *psz_url = NULL;
+ char *psz_val;
+
+ char *psz_accessname;
+ char *psz_parser;
+ const char *psz_user;
+ char *psz_pass;
+ char *psz_host;
+ char *psz_mount;
+ char *psz_port;
+ char *psz_name;
+ char *psz_description;
+ char *psz_genre;
+ char *psz_url;
config_ChainParse( p_access, SOUT_CFG_PREFIX, ppsz_sout_options, p_access->p_cfg );
- psz_accessname = psz_parser = strdup( p_access->psz_path );
-
if( !p_access->psz_path )
{
msg_Err( p_access,
return VLC_EGENERIC;
}
- /* Parse connection data user:pwd@host:port/mountpoint */
- psz_user = psz_parser;
- while( psz_parser[0] && psz_parser[0] != ':' ) psz_parser++;
- if( psz_parser[0] ) { psz_parser[0] = 0; psz_parser++; }
- psz_pass = psz_parser;
- while( psz_parser[0] && psz_parser[0] != '@' ) psz_parser++;
- if( psz_parser[0] ) { psz_parser[0] = 0; psz_parser++; }
- psz_host = psz_parser;
- while( psz_parser[0] && psz_parser[0] != ':' ) psz_parser++;
- if( psz_parser[0] ) { psz_parser[0] = 0; psz_parser++; }
- tmp_port = psz_parser;
- while( psz_parser[0] && psz_parser[0] != '/' ) psz_parser++;
- if( psz_parser[0] ) { psz_parser[0] = 0; psz_parser++; }
- psz_mount = psz_parser;
-
- i_port = atoi( tmp_port );
+ psz_accessname = psz_parser = strdup( p_access->psz_path );
+ if( !psz_parser )
+ return VLC_ENOMEM;
- p_sys = p_access->p_sys = malloc( sizeof( sout_access_out_sys_t ) );
- if( !p_sys )
+ /* Parse connection data user:pwd@host:port/mountpoint */
+ psz_host = strchr( psz_parser, '@' );
+ if( psz_host )
{
- msg_Err( p_access, "out of memory" );
- free( psz_accessname );
- return VLC_ENOMEM;
+ psz_user = psz_parser;
+ *(psz_host++) = '\0';
}
-
- var_Get( p_access, SOUT_CFG_PREFIX "name", &val );
- if( *val.psz_string )
- psz_name = val.psz_string;
else
- free( val.psz_string );
+ psz_user = "";
- var_Get( p_access, SOUT_CFG_PREFIX "description", &val );
- if( *val.psz_string )
- psz_description = val.psz_string;
+ psz_pass = strchr( psz_user, ':' );
+ if( psz_pass )
+ *(psz_pass++) = '\0';
else
- free( val.psz_string );
+ psz_pass = "";
- var_Get( p_access, SOUT_CFG_PREFIX "genre", &val );
- if( *val.psz_string )
- psz_genre = val.psz_string;
+ psz_mount = strchr( psz_host, '/' );
+ if( psz_mount )
+ *(psz_mount++) = '\0';
else
- free( val.psz_string );
+ psz_mount = "";
- var_Get( p_access, SOUT_CFG_PREFIX "url", &val );
- if( *val.psz_string )
- psz_url = val.psz_string;
+ if( psz_host[0] == '[' )
+ {
+ psz_port = strstr( psz_host, "]:" );
+ if( psz_port )
+ {
+ *psz_port = '\0';
+ psz_port += 2;
+ }
+ }
else
- free( val.psz_string );
+ {
+ psz_port = strchr( psz_host, ':' );
+ if( psz_port )
+ *(psz_port++) = '\0';
+ }
+ i_port = psz_port ? atoi( psz_port ) : 8000;
+
+ p_sys = p_access->p_sys = malloc( sizeof( sout_access_out_sys_t ) );
+ if( !p_sys )
+ {
+ free( psz_accessname );
+ return VLC_ENOMEM;
+ }
+
+ psz_name = var_GetNonEmptyString( p_access, SOUT_CFG_PREFIX "name" );
+ psz_description = var_GetNonEmptyString( p_access, SOUT_CFG_PREFIX "description" );
+ psz_genre = var_GetNonEmptyString( p_access, SOUT_CFG_PREFIX "genre" );
+ psz_url = var_GetNonEmptyString( p_access, SOUT_CFG_PREFIX "url" );
p_shout = p_sys->p_shout = shout_new();
if( !p_shout
psz_host, i_port, psz_mount );
free( p_access->p_sys );
free( psz_accessname );
+ free( psz_name );
+ free( psz_description );
+ free( psz_genre );
+ free( psz_url );
return VLC_EGENERIC;
}
- if( psz_name ) free( psz_name );
- if( psz_description ) free( psz_description );
- if( psz_genre ) free( psz_genre );
- if( psz_url ) free( psz_url );
+ free( psz_name );
+ free( psz_description );
+ free( psz_genre );
+ free( psz_url );
- var_Get( p_access, SOUT_CFG_PREFIX "mp3", &val );
- if( val.b_bool == VLC_TRUE )
+ if( var_GetBool( p_access, SOUT_CFG_PREFIX "mp3" ) )
i_ret = shout_set_format( p_shout, SHOUT_FORMAT_MP3 );
else
i_ret = shout_set_format( p_shout, SHOUT_FORMAT_OGG );
if( i_ret != SHOUTERR_SUCCESS )
{
msg_Err( p_access, "failed to set the shoutcast streaming format" );
- free( p_access->p_sys );
- free( psz_accessname );
- return VLC_EGENERIC;
+ goto error;
}
/* Don't force bitrate to 0 but only use when specified. This will otherwise
show an empty field on icecast directory listing instead of NA */
- var_Get( p_access, SOUT_CFG_PREFIX "bitrate", &val );
- if( *val.psz_string )
+ psz_val = var_GetNonEmptyString( p_access, SOUT_CFG_PREFIX "bitrate" );
+ if( psz_val )
{
- i_ret = shout_set_audio_info( p_shout, SHOUT_AI_BITRATE, val.psz_string );
+ i_ret = shout_set_audio_info( p_shout, SHOUT_AI_BITRATE, psz_val );
+ free( psz_val );
if( i_ret != SHOUTERR_SUCCESS )
{
msg_Err( p_access, "failed to set the information about the bitrate" );
- free( p_access->p_sys );
- free( psz_accessname );
- return VLC_EGENERIC;
+ goto error;
}
}
else
listings (sorting, stream info etc.) */
msg_Warn( p_access, "no bitrate information specified (required for listing " \
"the server as public on the shoutcast website)" );
- free( val.psz_string );
}
/* Information about samplerate, channels and quality will not be propagated
through the YP protocol for icecast to the public directory listing when
the icecast server is operating in shoutcast compatibility mode */
- var_Get( p_access, SOUT_CFG_PREFIX "samplerate", &val );
- if( *val.psz_string )
+ psz_val = var_GetNonEmptyString( p_access, SOUT_CFG_PREFIX "samplerate" );
+ if( psz_val )
{
- i_ret = shout_set_audio_info( p_shout, SHOUT_AI_SAMPLERATE, val.psz_string );
+ i_ret = shout_set_audio_info( p_shout, SHOUT_AI_SAMPLERATE, psz_val );
+ free( psz_val );
if( i_ret != SHOUTERR_SUCCESS )
{
msg_Err( p_access, "failed to set the information about the samplerate" );
- free( p_access->p_sys );
- free( psz_accessname );
- return VLC_EGENERIC;
+ goto error;
}
}
- else
- free( val.psz_string );
- var_Get( p_access, SOUT_CFG_PREFIX "channels", &val );
- if( *val.psz_string )
+ psz_val = var_GetNonEmptyString( p_access, SOUT_CFG_PREFIX "channels" );
+ if( psz_val )
{
- i_ret = shout_set_audio_info( p_shout, SHOUT_AI_CHANNELS, val.psz_string );
+ i_ret = shout_set_audio_info( p_shout, SHOUT_AI_CHANNELS, psz_val );
+ free( psz_val );
if( i_ret != SHOUTERR_SUCCESS )
{
msg_Err( p_access, "failed to set the information about the number of channels" );
- free( p_access->p_sys );
- free( psz_accessname );
- return VLC_EGENERIC;
+ goto error;
}
}
- else
- free( val.psz_string );
- var_Get( p_access, SOUT_CFG_PREFIX "quality", &val );
- if( *val.psz_string )
+ psz_val = var_GetNonEmptyString( p_access, SOUT_CFG_PREFIX "quality" );
+ if( psz_val )
{
- i_ret = shout_set_audio_info( p_shout, SHOUT_AI_QUALITY, val.psz_string );
+ i_ret = shout_set_audio_info( p_shout, SHOUT_AI_QUALITY, psz_val );
+ free( psz_val );
if( i_ret != SHOUTERR_SUCCESS )
{
msg_Err( p_access, "failed to set the information about Ogg Vorbis quality" );
- free( p_access->p_sys );
- free( psz_accessname );
- return VLC_EGENERIC;
+ goto error;
}
}
- else
- free( val.psz_string );
- var_Get( p_access, SOUT_CFG_PREFIX "public", &val );
- if( val.b_bool == VLC_TRUE )
+ if( var_GetBool( p_access, SOUT_CFG_PREFIX "public" ) )
{
i_ret = shout_set_public( p_shout, 1 );
if( i_ret != SHOUTERR_SUCCESS )
{
msg_Err( p_access, "failed to set the server status setting to public" );
- free( p_access->p_sys );
- free( psz_accessname );
- return VLC_EGENERIC;
+ goto error;
}
}
- /* Shoutcast using ICY protocol */
- i_ret = shout_open( p_shout );
- if( i_ret == SHOUTERR_SUCCESS )
+ /* Connect at startup. Cycle through the possible protocols. */
+ i_ret = shout_get_connected( p_shout );
+ while ( i_ret != SHOUTERR_CONNECTED )
{
- i_ret = SHOUTERR_CONNECTED;
- msg_Dbg( p_access, "connected using 'icy' (shoutcast) protocol" );
- }
- else
- {
- msg_Warn( p_access, "failed to connect using 'icy' (shoutcast) protocol" );
-
/* Shout parameters cannot be changed on an open connection */
i_ret = shout_close( p_shout );
if( i_ret == SHOUTERR_SUCCESS )
i_ret = SHOUTERR_UNCONNECTED;
}
- /* IceCAST using HTTP protocol */
- i_ret = shout_set_protocol( p_shout, SHOUT_PROTOCOL_HTTP );
+ /* Re-initialize for Shoutcast using ICY protocol. Not needed for initial connection
+ but it is when we are reconnecting after other protocol was tried. */
+ i_ret = shout_set_protocol( p_shout, SHOUT_PROTOCOL_ICY );
if( i_ret != SHOUTERR_SUCCESS )
{
- msg_Err( p_access, "failed to set the protocol to 'http'" );
- free( p_access->p_sys );
- free( psz_accessname );
- return VLC_EGENERIC;
+ msg_Err( p_access, "failed to set the protocol to 'icy'" );
+ goto error;
}
-
i_ret = shout_open( p_shout );
if( i_ret == SHOUTERR_SUCCESS )
{
i_ret = SHOUTERR_CONNECTED;
- msg_Dbg( p_access, "connected using 'http' (icecast 2.x) protocol" );
+ msg_Dbg( p_access, "connected using 'icy' (shoutcast) protocol" );
}
else
- msg_Warn( p_access, "failed to connect using 'http' (icecast 2.x) protocol " );
- }
-
+ {
+ msg_Warn( p_access, "failed to connect using 'icy' (shoutcast) protocol" );
+
+ /* Shout parameters cannot be changed on an open connection */
+ i_ret = shout_close( p_shout );
+ if( i_ret == SHOUTERR_SUCCESS )
+ {
+ i_ret = SHOUTERR_UNCONNECTED;
+ }
+
+ /* IceCAST using HTTP protocol */
+ i_ret = shout_set_protocol( p_shout, SHOUT_PROTOCOL_HTTP );
+ if( i_ret != SHOUTERR_SUCCESS )
+ {
+ msg_Err( p_access, "failed to set the protocol to 'http'" );
+ goto error;
+ }
+ i_ret = shout_open( p_shout );
+ if( i_ret == SHOUTERR_SUCCESS )
+ {
+ i_ret = SHOUTERR_CONNECTED;
+ msg_Dbg( p_access, "connected using 'http' (icecast 2.x) protocol" );
+ }
+ else
+ msg_Warn( p_access, "failed to connect using 'http' (icecast 2.x) protocol " );
+ }
/*
- for non-blocking, use:
- while( i_ret == SHOUTERR_BUSY )
- {
- sleep( 1 );
- i_ret = shout_get_connected( p_shout );
- }
+ for non-blocking, use:
+ while( i_ret == SHOUTERR_BUSY )
+ {
+ sleep( 1 );
+ i_ret = shout_get_connected( p_shout );
+ }
*/
+ if ( i_ret != SHOUTERR_CONNECTED )
+ {
+ msg_Warn( p_access, "unable to establish connection, retrying..." );
+ msleep( 30000000 );
+ }
+ }
+
if( i_ret != SHOUTERR_CONNECTED )
{
msg_Err( p_access, "failed to open shout stream to %s:%i/%s: %s",
}
p_access->pf_write = Write;
- p_access->pf_read = Read;
p_access->pf_seek = Seek;
+ p_access->pf_control = Control;
msg_Dbg( p_access, "shout access output opened (%s@%s:%i/%s)",
psz_user, psz_host, i_port, psz_mount );
-
- /* Update pace control flag */
- if( p_access->psz_access && !strcmp( p_access->psz_access, "stream" ) )
- {
- p_access->p_sout->i_out_pace_nocontrol++;
- }
-
free( psz_accessname );
return VLC_SUCCESS;
+
+error:
+ free( psz_accessname );
+ free( p_sys );
+ return VLC_EGENERIC;
}
/*****************************************************************************
shout_shutdown();
}
free( p_access->p_sys );
-
- /* Update pace control flag */
- if( p_access->psz_access && !strcmp( p_access->psz_access, "stream" ) )
- {
- p_access->p_sout->i_out_pace_nocontrol--;
- }
-
msg_Dbg( p_access, "shout access output closed" );
}
-/*****************************************************************************
- * Read: standard read -- not supported
- *****************************************************************************/
-static int Read( sout_access_out_t *p_access, block_t *p_buffer )
+static int Control( sout_access_out_t *p_access, int i_query, va_list args )
{
- msg_Err( p_access, "cannot read from shout" );
- return VLC_EGENERIC;
+ switch( i_query )
+ {
+ case ACCESS_OUT_CONTROLS_PACE:
+ {
+ bool *pb = va_arg( args, bool * );
+ *pb = strcmp( p_access->psz_access, "stream" );
+ break;
+ }
+
+ default:
+ return VLC_EGENERIC;
+ }
+ return VLC_SUCCESS;
}
/*****************************************************************************
* Write: standard write
*****************************************************************************/
-static int Write( sout_access_out_t *p_access, block_t *p_buffer )
+static ssize_t Write( sout_access_out_t *p_access, block_t *p_buffer )
{
size_t i_write = 0;
{
msg_Err( p_access, "cannot write to stream: %s",
shout_get_error(p_access->p_sys->p_shout) );
+
+ /* The most common cause seems to be a server disconnect, resulting in a
+ Socket Error which can only be fixed by closing and reconnecting.
+ Since we already began with a working connection, the most feasable
+ approach to get out of this error status is a (timed) reconnect approach. */
+ shout_close( p_access->p_sys->p_shout );
+ msg_Warn( p_access, "server unavailable? trying to reconnect..." );
+ /* Re-open the connection (protocol params have already been set) and re-sync */
+ if( shout_open( p_access->p_sys->p_shout ) == SHOUTERR_SUCCESS )
+ {
+ shout_sync( p_access->p_sys->p_shout );
+ msg_Warn( p_access, "reconnected to server" );
+ }
+ else
+ {
+ msg_Err( p_access, "failed to reconnect to server" );
+ block_ChainRelease (p_buffer);
+ return VLC_EGENERIC;
+ }
+
}
block_Release( p_buffer );
*****************************************************************************/
static int Seek( sout_access_out_t *p_access, off_t i_pos )
{
+ VLC_UNUSED(i_pos);
msg_Err( p_access, "cannot seek on shout" );
return VLC_EGENERIC;
}