1 /*****************************************************************************
2 * shout.c: This module forwards vorbis streams to an icecast server
3 *****************************************************************************
4 * Copyright (C) 2005 the VideoLAN team
7 * Authors: Daniel Fischer <dan at subsignal dot org>
8 * Derk-Jan Hartman <hartman at videolan dot org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
28 * - this only works for ogg and/or mp3, and we don't check this yet.
29 * - MP3 metadata is not passed along, since metadata is only available after
30 * this module is opened.
34 * vlc v4l:/dev/video:input=2:norm=pal:size=192x144 \
35 * --sout '#transcode{vcodec=theora,vb=300,acodec=vorb,ab=96}\
36 * :std{access=shout,mux=ogg,dst=localhost:8005}'
38 *****************************************************************************/
40 /*****************************************************************************
42 *****************************************************************************/
46 #include <vlc_block.h>
48 #include <shout/shout.h>
50 /*****************************************************************************
52 *****************************************************************************/
53 static int Open ( vlc_object_t * );
54 static void Close( vlc_object_t * );
56 #define SOUT_CFG_PREFIX "sout-shout-"
58 #define NAME_TEXT N_("Stream name")
59 #define NAME_LONGTEXT N_("Name to give to this stream/channel on the " \
60 "shoutcast/icecast server." )
62 #define DESCRIPTION_TEXT N_("Stream description")
63 #define DESCRIPTION_LONGTEXT N_("Description of the stream content or " \
64 "information about your channel." )
66 #define MP3_TEXT N_("Stream MP3")
67 #define MP3_LONGTEXT N_("You normally have to feed the shoutcast module " \
68 "with Ogg streams. It is also possible to stream " \
69 "MP3 instead, so you can forward MP3 streams to " \
70 "the shoutcast/icecast server." )
72 /* To be listed properly as a public stream on the Yellow Pages of shoutcast/icecast
73 the genres should match those used on the corresponding sites. Several examples
74 are Alternative, Classical, Comedy, Country etc. */
76 #define GENRE_TEXT N_("Genre description")
77 #define GENRE_LONGTEXT N_("Genre of the content. " )
79 #define URL_TEXT N_("URL description")
80 #define URL_LONGTEXT N_("URL with information about the stream or your channel. " )
82 /* The shout module only "transmits" data. It does not have direct access to
83 "codec level" information. Stream information such as bitrate, samplerate,
84 channel numbers and quality (in case of Ogg streaming) need to be set manually */
86 #define BITRATE_TEXT N_("Bitrate")
87 #define BITRATE_LONGTEXT N_("Bitrate information of the transcoded stream. " )
89 #define SAMPLERATE_TEXT N_("Samplerate")
90 #define SAMPLERATE_LONGTEXT N_("Samplerate information of the transcoded stream. " )
92 #define CHANNELS_TEXT N_("Number of channels")
93 #define CHANNELS_LONGTEXT N_("Number of channels information of the transcoded stream. " )
95 #define QUALITY_TEXT N_("Ogg Vorbis Quality")
96 #define QUALITY_LONGTEXT N_("Ogg Vorbis Quality information of the transcoded stream. " )
98 #define PUBLIC_TEXT N_("Stream public")
99 #define PUBLIC_LONGTEXT N_("Make the server publicly available on the 'Yellow Pages' " \
100 "(directory listing of streams) on the icecast/shoutcast " \
101 "website. Requires the bitrate information specified for " \
102 "shoutcast. Requires Ogg streaming for icecast." )
105 set_description( _("IceCAST output") );
106 set_shortname( "Shoutcast" );
107 set_capability( "sout access", 50 );
108 set_category( CAT_SOUT );
109 set_subcategory( SUBCAT_SOUT_ACO );
110 add_shortcut( "shout" );
111 add_string( SOUT_CFG_PREFIX "name", "VLC media player - Live stream", NULL,
112 NAME_TEXT, NAME_LONGTEXT, VLC_FALSE );
113 add_string( SOUT_CFG_PREFIX "description",
114 "Live stream from VLC media player", NULL,
115 DESCRIPTION_TEXT, DESCRIPTION_LONGTEXT, VLC_FALSE );
116 add_bool( SOUT_CFG_PREFIX "mp3", VLC_FALSE, NULL,
117 MP3_TEXT, MP3_LONGTEXT, VLC_TRUE );
118 add_string( SOUT_CFG_PREFIX "genre", "Alternative", NULL,
119 GENRE_TEXT, GENRE_LONGTEXT, VLC_FALSE );
120 add_string( SOUT_CFG_PREFIX "url", "http://www.videolan.org/vlc", NULL,
121 URL_TEXT, URL_LONGTEXT, VLC_FALSE );
122 add_string( SOUT_CFG_PREFIX "bitrate", "", NULL,
123 BITRATE_TEXT, BITRATE_LONGTEXT, VLC_FALSE );
124 add_string( SOUT_CFG_PREFIX "samplerate", "", NULL,
125 SAMPLERATE_TEXT, SAMPLERATE_LONGTEXT, VLC_FALSE );
126 add_string( SOUT_CFG_PREFIX "channels", "", NULL,
127 CHANNELS_TEXT, CHANNELS_LONGTEXT, VLC_FALSE );
128 add_string( SOUT_CFG_PREFIX "quality", "", NULL,
129 QUALITY_TEXT, QUALITY_LONGTEXT, VLC_FALSE );
130 add_bool( SOUT_CFG_PREFIX "public", VLC_FALSE, NULL,
131 PUBLIC_TEXT, PUBLIC_LONGTEXT, VLC_TRUE );
132 set_callbacks( Open, Close );
135 /*****************************************************************************
136 * Exported prototypes
137 *****************************************************************************/
138 static const char *ppsz_sout_options[] = {
139 "name", "description", "mp3", "genre", "url", "bitrate", "samplerate",
140 "channels", "quality", "public", NULL
144 /*****************************************************************************
145 * Exported prototypes
146 *****************************************************************************/
147 static int Write( sout_access_out_t *, block_t * );
148 static int Seek ( sout_access_out_t *, off_t );
149 static int Read ( sout_access_out_t *, block_t * );
151 struct sout_access_out_sys_t
156 /*****************************************************************************
157 * Open: open the shout connection
158 *****************************************************************************/
159 static int Open( vlc_object_t *p_this )
161 sout_access_out_t *p_access = (sout_access_out_t*)p_this;
162 sout_access_out_sys_t *p_sys;
168 char *psz_accessname = NULL;
169 char *psz_parser = NULL;
170 char *psz_user = NULL;
171 char *psz_pass = NULL;
172 char *psz_host = NULL;
173 char *psz_mount = NULL;
174 char *psz_name = NULL;
175 char *psz_description = NULL;
176 char *tmp_port = NULL;
177 char *psz_genre = NULL;
178 char *psz_url = NULL;
180 config_ChainParse( p_access, SOUT_CFG_PREFIX, ppsz_sout_options, p_access->p_cfg );
182 psz_accessname = psz_parser = strdup( p_access->psz_path );
184 if( !p_access->psz_path )
187 "please specify url=user:password@host:port/mountpoint" );
191 /* Parse connection data user:pwd@host:port/mountpoint */
192 psz_user = psz_parser;
193 while( psz_parser[0] && psz_parser[0] != ':' ) psz_parser++;
194 if( psz_parser[0] ) { psz_parser[0] = 0; psz_parser++; }
195 psz_pass = psz_parser;
196 while( psz_parser[0] && psz_parser[0] != '@' ) psz_parser++;
197 if( psz_parser[0] ) { psz_parser[0] = 0; psz_parser++; }
198 psz_host = psz_parser;
199 while( psz_parser[0] && psz_parser[0] != ':' ) psz_parser++;
200 if( psz_parser[0] ) { psz_parser[0] = 0; psz_parser++; }
201 tmp_port = psz_parser;
202 while( psz_parser[0] && psz_parser[0] != '/' ) psz_parser++;
203 if( psz_parser[0] ) { psz_parser[0] = 0; psz_parser++; }
204 psz_mount = psz_parser;
206 i_port = atoi( tmp_port );
208 p_sys = p_access->p_sys = malloc( sizeof( sout_access_out_sys_t ) );
211 msg_Err( p_access, "out of memory" );
212 free( psz_accessname );
216 var_Get( p_access, SOUT_CFG_PREFIX "name", &val );
217 if( *val.psz_string )
218 psz_name = val.psz_string;
220 free( val.psz_string );
222 var_Get( p_access, SOUT_CFG_PREFIX "description", &val );
223 if( *val.psz_string )
224 psz_description = val.psz_string;
226 free( val.psz_string );
228 var_Get( p_access, SOUT_CFG_PREFIX "genre", &val );
229 if( *val.psz_string )
230 psz_genre = val.psz_string;
232 free( val.psz_string );
234 var_Get( p_access, SOUT_CFG_PREFIX "url", &val );
235 if( *val.psz_string )
236 psz_url = val.psz_string;
238 free( val.psz_string );
240 p_shout = p_sys->p_shout = shout_new();
242 || shout_set_host( p_shout, psz_host ) != SHOUTERR_SUCCESS
243 || shout_set_protocol( p_shout, SHOUT_PROTOCOL_ICY ) != SHOUTERR_SUCCESS
244 || shout_set_port( p_shout, i_port ) != SHOUTERR_SUCCESS
245 || shout_set_password( p_shout, psz_pass ) != SHOUTERR_SUCCESS
246 || shout_set_mount( p_shout, psz_mount ) != SHOUTERR_SUCCESS
247 || shout_set_user( p_shout, psz_user ) != SHOUTERR_SUCCESS
248 || shout_set_agent( p_shout, "VLC media player " VERSION ) != SHOUTERR_SUCCESS
249 || shout_set_name( p_shout, psz_name ) != SHOUTERR_SUCCESS
250 || shout_set_description( p_shout, psz_description ) != SHOUTERR_SUCCESS
251 || shout_set_genre( p_shout, psz_genre ) != SHOUTERR_SUCCESS
252 || shout_set_url( p_shout, psz_url ) != SHOUTERR_SUCCESS
253 /* || shout_set_nonblocking( p_shout, 1 ) != SHOUTERR_SUCCESS */
256 msg_Err( p_access, "failed to initialize shout streaming to %s:%i/%s",
257 psz_host, i_port, psz_mount );
258 free( p_access->p_sys );
259 free( psz_accessname );
263 if( psz_name ) free( psz_name );
264 if( psz_description ) free( psz_description );
265 if( psz_genre ) free( psz_genre );
266 if( psz_url ) free( psz_url );
268 var_Get( p_access, SOUT_CFG_PREFIX "mp3", &val );
269 if( val.b_bool == VLC_TRUE )
270 i_ret = shout_set_format( p_shout, SHOUT_FORMAT_MP3 );
272 i_ret = shout_set_format( p_shout, SHOUT_FORMAT_OGG );
274 if( i_ret != SHOUTERR_SUCCESS )
276 msg_Err( p_access, "failed to set the shoutcast streaming format" );
277 free( p_access->p_sys );
278 free( psz_accessname );
282 /* Don't force bitrate to 0 but only use when specified. This will otherwise
283 show an empty field on icecast directory listing instead of NA */
284 var_Get( p_access, SOUT_CFG_PREFIX "bitrate", &val );
285 if( *val.psz_string )
287 i_ret = shout_set_audio_info( p_shout, SHOUT_AI_BITRATE, val.psz_string );
288 if( i_ret != SHOUTERR_SUCCESS )
290 msg_Err( p_access, "failed to set the information about the bitrate" );
291 free( p_access->p_sys );
292 free( psz_accessname );
298 /* Bitrate information is used for icecast/shoutcast servers directory
299 listings (sorting, stream info etc.) */
300 msg_Warn( p_access, "no bitrate information specified (required for listing " \
301 "the server as public on the shoutcast website)" );
302 free( val.psz_string );
305 /* Information about samplerate, channels and quality will not be propagated
306 through the YP protocol for icecast to the public directory listing when
307 the icecast server is operating in shoutcast compatibility mode */
309 var_Get( p_access, SOUT_CFG_PREFIX "samplerate", &val );
310 if( *val.psz_string )
312 i_ret = shout_set_audio_info( p_shout, SHOUT_AI_SAMPLERATE, val.psz_string );
313 if( i_ret != SHOUTERR_SUCCESS )
315 msg_Err( p_access, "failed to set the information about the samplerate" );
316 free( p_access->p_sys );
317 free( psz_accessname );
322 free( val.psz_string );
324 var_Get( p_access, SOUT_CFG_PREFIX "channels", &val );
325 if( *val.psz_string )
327 i_ret = shout_set_audio_info( p_shout, SHOUT_AI_CHANNELS, val.psz_string );
328 if( i_ret != SHOUTERR_SUCCESS )
330 msg_Err( p_access, "failed to set the information about the number of channels" );
331 free( p_access->p_sys );
332 free( psz_accessname );
337 free( val.psz_string );
339 var_Get( p_access, SOUT_CFG_PREFIX "quality", &val );
340 if( *val.psz_string )
342 i_ret = shout_set_audio_info( p_shout, SHOUT_AI_QUALITY, val.psz_string );
343 if( i_ret != SHOUTERR_SUCCESS )
345 msg_Err( p_access, "failed to set the information about Ogg Vorbis quality" );
346 free( p_access->p_sys );
347 free( psz_accessname );
352 free( val.psz_string );
354 var_Get( p_access, SOUT_CFG_PREFIX "public", &val );
355 if( val.b_bool == VLC_TRUE )
357 i_ret = shout_set_public( p_shout, 1 );
358 if( i_ret != SHOUTERR_SUCCESS )
360 msg_Err( p_access, "failed to set the server status setting to public" );
361 free( p_access->p_sys );
362 free( psz_accessname );
367 /* Shoutcast using ICY protocol */
368 i_ret = shout_open( p_shout );
369 if( i_ret == SHOUTERR_SUCCESS )
371 i_ret = SHOUTERR_CONNECTED;
372 msg_Dbg( p_access, "connected using 'icy' (shoutcast) protocol" );
376 msg_Warn( p_access, "failed to connect using 'icy' (shoutcast) protocol" );
378 /* Shout parameters cannot be changed on an open connection */
379 i_ret = shout_close( p_shout );
380 if( i_ret == SHOUTERR_SUCCESS )
382 i_ret = SHOUTERR_UNCONNECTED;
385 /* IceCAST using HTTP protocol */
386 i_ret = shout_set_protocol( p_shout, SHOUT_PROTOCOL_HTTP );
387 if( i_ret != SHOUTERR_SUCCESS )
389 msg_Err( p_access, "failed to set the protocol to 'http'" );
390 free( p_access->p_sys );
391 free( psz_accessname );
395 i_ret = shout_open( p_shout );
396 if( i_ret == SHOUTERR_SUCCESS )
398 i_ret = SHOUTERR_CONNECTED;
399 msg_Dbg( p_access, "connected using 'http' (icecast 2.x) protocol" );
402 msg_Warn( p_access, "failed to connect using 'http' (icecast 2.x) protocol " );
406 for non-blocking, use:
407 while( i_ret == SHOUTERR_BUSY )
410 i_ret = shout_get_connected( p_shout );
413 if( i_ret != SHOUTERR_CONNECTED )
415 msg_Err( p_access, "failed to open shout stream to %s:%i/%s: %s",
416 psz_host, i_port, psz_mount, shout_get_error(p_shout) );
417 free( p_access->p_sys );
418 free( psz_accessname );
422 p_access->pf_write = Write;
423 p_access->pf_read = Read;
424 p_access->pf_seek = Seek;
426 msg_Dbg( p_access, "shout access output opened (%s@%s:%i/%s)",
427 psz_user, psz_host, i_port, psz_mount );
429 /* Update pace control flag */
430 if( p_access->psz_access && !strcmp( p_access->psz_access, "stream" ) )
432 p_access->p_sout->i_out_pace_nocontrol++;
435 free( psz_accessname );
440 /*****************************************************************************
441 * Close: close the target
442 *****************************************************************************/
443 static void Close( vlc_object_t * p_this )
445 sout_access_out_t *p_access = (sout_access_out_t*)p_this;
447 if( p_access->p_sys && p_access->p_sys->p_shout )
449 shout_close( p_access->p_sys->p_shout );
452 free( p_access->p_sys );
454 /* Update pace control flag */
455 if( p_access->psz_access && !strcmp( p_access->psz_access, "stream" ) )
457 p_access->p_sout->i_out_pace_nocontrol--;
460 msg_Dbg( p_access, "shout access output closed" );
463 /*****************************************************************************
464 * Read: standard read -- not supported
465 *****************************************************************************/
466 static int Read( sout_access_out_t *p_access, block_t *p_buffer )
468 msg_Err( p_access, "cannot read from shout" );
472 /*****************************************************************************
473 * Write: standard write
474 *****************************************************************************/
475 static int Write( sout_access_out_t *p_access, block_t *p_buffer )
479 shout_sync( p_access->p_sys->p_shout );
482 block_t *p_next = p_buffer->p_next;
484 if( shout_send( p_access->p_sys->p_shout,
485 p_buffer->p_buffer, p_buffer->i_buffer )
486 == SHOUTERR_SUCCESS )
488 i_write += p_buffer->i_buffer;
492 msg_Err( p_access, "cannot write to stream: %s",
493 shout_get_error(p_access->p_sys->p_shout) );
495 block_Release( p_buffer );
497 /* XXX: Unsure if that's the cause for some audio trouble... */
505 /*****************************************************************************
506 * Seek: seek to a specific location -- not supported
507 *****************************************************************************/
508 static int Seek( sout_access_out_t *p_access, off_t i_pos )
510 msg_Err( p_access, "cannot seek on shout" );