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 *****************************************************************************/
47 #include <vlc_block.h>
49 #include <shout/shout.h>
51 /*****************************************************************************
53 *****************************************************************************/
54 static int Open ( vlc_object_t * );
55 static void Close( vlc_object_t * );
57 #define SOUT_CFG_PREFIX "sout-shout-"
59 #define NAME_TEXT N_("Stream name")
60 #define NAME_LONGTEXT N_("Name to give to this stream/channel on the " \
61 "shoutcast/icecast server." )
63 #define DESCRIPTION_TEXT N_("Stream description")
64 #define DESCRIPTION_LONGTEXT N_("Description of the stream content or " \
65 "information about your channel." )
67 #define MP3_TEXT N_("Stream MP3")
68 #define MP3_LONGTEXT N_("You normally have to feed the shoutcast module " \
69 "with Ogg streams. It is also possible to stream " \
70 "MP3 instead, so you can forward MP3 streams to " \
71 "the shoutcast/icecast server." )
73 /* To be listed properly as a public stream on the Yellow Pages of shoutcast/icecast
74 the genres should match those used on the corresponding sites. Several examples
75 are Alternative, Classical, Comedy, Country etc. */
77 #define GENRE_TEXT N_("Genre description")
78 #define GENRE_LONGTEXT N_("Genre of the content. " )
80 #define URL_TEXT N_("URL description")
81 #define URL_LONGTEXT N_("URL with information about the stream or your channel. " )
83 /* The shout module only "transmits" data. It does not have direct access to
84 "codec level" information. Stream information such as bitrate, samplerate,
85 channel numbers and quality (in case of Ogg streaming) need to be set manually */
87 #define BITRATE_TEXT N_("Bitrate")
88 #define BITRATE_LONGTEXT N_("Bitrate information of the transcoded stream. " )
90 #define SAMPLERATE_TEXT N_("Samplerate")
91 #define SAMPLERATE_LONGTEXT N_("Samplerate information of the transcoded stream. " )
93 #define CHANNELS_TEXT N_("Number of channels")
94 #define CHANNELS_LONGTEXT N_("Number of channels information of the transcoded stream. " )
96 #define QUALITY_TEXT N_("Ogg Vorbis Quality")
97 #define QUALITY_LONGTEXT N_("Ogg Vorbis Quality information of the transcoded stream. " )
99 #define PUBLIC_TEXT N_("Stream public")
100 #define PUBLIC_LONGTEXT N_("Make the server publicly available on the 'Yellow Pages' " \
101 "(directory listing of streams) on the icecast/shoutcast " \
102 "website. Requires the bitrate information specified for " \
103 "shoutcast. Requires Ogg streaming for icecast." )
106 set_description( _("IceCAST output") );
107 set_shortname( "Shoutcast" );
108 set_capability( "sout access", 50 );
109 set_category( CAT_SOUT );
110 set_subcategory( SUBCAT_SOUT_ACO );
111 add_shortcut( "shout" );
112 add_string( SOUT_CFG_PREFIX "name", "VLC media player - Live stream", NULL,
113 NAME_TEXT, NAME_LONGTEXT, VLC_FALSE );
114 add_string( SOUT_CFG_PREFIX "description",
115 "Live stream from VLC media player", NULL,
116 DESCRIPTION_TEXT, DESCRIPTION_LONGTEXT, VLC_FALSE );
117 add_bool( SOUT_CFG_PREFIX "mp3", VLC_FALSE, NULL,
118 MP3_TEXT, MP3_LONGTEXT, VLC_TRUE );
119 add_string( SOUT_CFG_PREFIX "genre", "Alternative", NULL,
120 GENRE_TEXT, GENRE_LONGTEXT, VLC_FALSE );
121 add_string( SOUT_CFG_PREFIX "url", "http://www.videolan.org/vlc", NULL,
122 URL_TEXT, URL_LONGTEXT, VLC_FALSE );
123 add_string( SOUT_CFG_PREFIX "bitrate", "", NULL,
124 BITRATE_TEXT, BITRATE_LONGTEXT, VLC_FALSE );
125 add_string( SOUT_CFG_PREFIX "samplerate", "", NULL,
126 SAMPLERATE_TEXT, SAMPLERATE_LONGTEXT, VLC_FALSE );
127 add_string( SOUT_CFG_PREFIX "channels", "", NULL,
128 CHANNELS_TEXT, CHANNELS_LONGTEXT, VLC_FALSE );
129 add_string( SOUT_CFG_PREFIX "quality", "", NULL,
130 QUALITY_TEXT, QUALITY_LONGTEXT, VLC_FALSE );
131 add_bool( SOUT_CFG_PREFIX "public", VLC_FALSE, NULL,
132 PUBLIC_TEXT, PUBLIC_LONGTEXT, VLC_TRUE );
133 set_callbacks( Open, Close );
136 /*****************************************************************************
137 * Exported prototypes
138 *****************************************************************************/
139 static const char *ppsz_sout_options[] = {
140 "name", "description", "mp3", "genre", "url", "bitrate", "samplerate",
141 "channels", "quality", "public", NULL
145 /*****************************************************************************
146 * Exported prototypes
147 *****************************************************************************/
148 static int Write( sout_access_out_t *, block_t * );
149 static int Seek ( sout_access_out_t *, off_t );
150 static int Read ( sout_access_out_t *, block_t * );
152 struct sout_access_out_sys_t
157 /*****************************************************************************
158 * Open: open the shout connection
159 *****************************************************************************/
160 static int Open( vlc_object_t *p_this )
162 sout_access_out_t *p_access = (sout_access_out_t*)p_this;
163 sout_access_out_sys_t *p_sys;
169 char *psz_accessname = NULL;
170 char *psz_parser = NULL;
171 char *psz_user = NULL;
172 char *psz_pass = NULL;
173 char *psz_host = NULL;
174 char *psz_mount = NULL;
175 char *psz_name = NULL;
176 char *psz_description = NULL;
177 char *tmp_port = NULL;
178 char *psz_genre = NULL;
179 char *psz_url = NULL;
181 config_ChainParse( p_access, SOUT_CFG_PREFIX, ppsz_sout_options, p_access->p_cfg );
183 psz_accessname = psz_parser = strdup( p_access->psz_name );
185 if( !p_access->psz_name )
188 "please specify url=user:password@host:port/mountpoint" );
192 /* Parse connection data user:pwd@host:port/mountpoint */
193 psz_user = psz_parser;
194 while( psz_parser[0] && psz_parser[0] != ':' ) psz_parser++;
195 if( psz_parser[0] ) { psz_parser[0] = 0; psz_parser++; }
196 psz_pass = psz_parser;
197 while( psz_parser[0] && psz_parser[0] != '@' ) psz_parser++;
198 if( psz_parser[0] ) { psz_parser[0] = 0; psz_parser++; }
199 psz_host = psz_parser;
200 while( psz_parser[0] && psz_parser[0] != ':' ) psz_parser++;
201 if( psz_parser[0] ) { psz_parser[0] = 0; psz_parser++; }
202 tmp_port = psz_parser;
203 while( psz_parser[0] && psz_parser[0] != '/' ) psz_parser++;
204 if( psz_parser[0] ) { psz_parser[0] = 0; psz_parser++; }
205 psz_mount = psz_parser;
207 i_port = atoi( tmp_port );
209 p_sys = p_access->p_sys = malloc( sizeof( sout_access_out_sys_t ) );
212 msg_Err( p_access, "out of memory" );
213 free( psz_accessname );
217 var_Get( p_access, SOUT_CFG_PREFIX "name", &val );
218 if( *val.psz_string )
219 psz_name = val.psz_string;
221 free( val.psz_string );
223 var_Get( p_access, SOUT_CFG_PREFIX "description", &val );
224 if( *val.psz_string )
225 psz_description = val.psz_string;
227 free( val.psz_string );
229 var_Get( p_access, SOUT_CFG_PREFIX "genre", &val );
230 if( *val.psz_string )
231 psz_genre = val.psz_string;
233 free( val.psz_string );
235 var_Get( p_access, SOUT_CFG_PREFIX "url", &val );
236 if( *val.psz_string )
237 psz_url = val.psz_string;
239 free( val.psz_string );
241 p_shout = p_sys->p_shout = shout_new();
243 || shout_set_host( p_shout, psz_host ) != SHOUTERR_SUCCESS
244 || shout_set_protocol( p_shout, SHOUT_PROTOCOL_HTTP ) != SHOUTERR_SUCCESS
245 || shout_set_port( p_shout, i_port ) != SHOUTERR_SUCCESS
246 || shout_set_password( p_shout, psz_pass ) != SHOUTERR_SUCCESS
247 || shout_set_mount( p_shout, psz_mount ) != SHOUTERR_SUCCESS
248 || shout_set_user( p_shout, psz_user ) != SHOUTERR_SUCCESS
249 || shout_set_agent( p_shout, "VLC media player " VERSION ) != SHOUTERR_SUCCESS
250 || shout_set_name( p_shout, psz_name ) != SHOUTERR_SUCCESS
251 || shout_set_description( p_shout, psz_description ) != SHOUTERR_SUCCESS
252 || shout_set_genre( p_shout, psz_genre ) != SHOUTERR_SUCCESS
253 || shout_set_url( p_shout, psz_url ) != SHOUTERR_SUCCESS
254 // || shout_set_nonblocking( p_shout, 1 ) != SHOUTERR_SUCCESS
257 msg_Err( p_access, "failed to initialize shout streaming to %s:%i/%s",
258 psz_host, i_port, psz_mount );
259 free( p_access->p_sys );
260 free( psz_accessname );
264 if( psz_name ) free( psz_name );
265 if( psz_description ) free( psz_description );
266 if( psz_genre ) free( psz_genre );
267 if( psz_url ) free( psz_url );
269 var_Get( p_access, SOUT_CFG_PREFIX "mp3", &val );
270 if( val.b_bool == VLC_TRUE )
271 i_ret = shout_set_format( p_shout, SHOUT_FORMAT_MP3 );
273 i_ret = shout_set_format( p_shout, SHOUT_FORMAT_OGG );
275 if( i_ret != SHOUTERR_SUCCESS )
277 msg_Err( p_access, "failed to set the shoutcast streaming format" );
278 free( p_access->p_sys );
279 free( psz_accessname );
284 /* Don't force bitrate to 0 but only use when specified. This will otherwise
285 show an empty field on icecast directory listing instead of NA */
286 var_Get( p_access, SOUT_CFG_PREFIX "bitrate", &val );
287 if( *val.psz_string )
289 i_ret = shout_set_audio_info( p_shout, SHOUT_AI_BITRATE, val.psz_string );
293 /* Bitrate information is used for icecast/shoutcast servers directory
294 listings (sorting, stream info etc.) */
295 msg_Warn( p_access, "no bitrate information specified (required for listing " \
296 "the server as public on the shoutcast website)" );
297 free( val.psz_string );
300 if( i_ret != SHOUTERR_SUCCESS )
302 msg_Err( p_access, "failed to set the information about the bitrate" );
303 free( p_access->p_sys );
304 free( psz_accessname );
308 /* Information about samplerate, channels and quality will not be propagated
309 through the YP protocol for icecast to the public directory listing when
310 the icecast server is operating in shoutcast compatibility mode */
312 var_Get( p_access, SOUT_CFG_PREFIX "samplerate", &val );
313 if( *val.psz_string )
314 i_ret = shout_set_audio_info( p_shout, SHOUT_AI_SAMPLERATE, val.psz_string );
316 free( val.psz_string );
318 if( i_ret != SHOUTERR_SUCCESS )
320 msg_Err( p_access, "failed to set the information about the samplerate" );
321 free( p_access->p_sys );
322 free( psz_accessname );
326 var_Get( p_access, SOUT_CFG_PREFIX "channels", &val );
327 if( *val.psz_string )
328 i_ret = shout_set_audio_info( p_shout, SHOUT_AI_CHANNELS, val.psz_string );
330 free( val.psz_string );
332 if( i_ret != SHOUTERR_SUCCESS )
334 msg_Err( p_access, "failed to set the information about the number of channels" );
335 free( p_access->p_sys );
336 free( psz_accessname );
340 var_Get( p_access, SOUT_CFG_PREFIX "quality", &val );
341 if( *val.psz_string )
342 i_ret = shout_set_audio_info( p_shout, SHOUT_AI_QUALITY, val.psz_string );
344 free( val.psz_string );
346 if( i_ret != SHOUTERR_SUCCESS )
348 msg_Err( p_access, "failed to set the information about Ogg Vorbis quality" );
349 free( p_access->p_sys );
350 free( psz_accessname );
354 var_Get( p_access, SOUT_CFG_PREFIX "public", &val );
355 if( val.b_bool == VLC_TRUE )
356 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 );
366 i_ret = shout_open( p_shout );
367 if( i_ret == SHOUTERR_SUCCESS )
369 i_ret = SHOUTERR_CONNECTED;
373 /* If default 'http' protocol for icecast 2.x fails, fall back to 'icy'
374 for shoutcast server */
375 msg_Warn( p_access, "failed to connect using 'http' (icecast 2.x) protocol, "
376 "switching to 'icy' (shoutcast)" );
378 i_ret = shout_get_format( p_shout );
379 if( i_ret != SHOUT_FORMAT_MP3 )
381 msg_Err( p_access, "failed to use 'icy' protocol: only MP3 " \
382 "streaming to shoutcast is supported" );
383 free( p_access->p_sys );
384 free( psz_accessname );
388 /* Shout parameters cannot be changed on an open connection */
389 i_ret = shout_close( p_shout );
390 if( i_ret == SHOUTERR_SUCCESS )
392 i_ret = SHOUTERR_UNCONNECTED;
395 i_ret = shout_set_protocol( p_shout, SHOUT_PROTOCOL_ICY );
396 if( i_ret != SHOUTERR_SUCCESS )
398 msg_Err( p_access, "failed to set the protocol to 'icy'" );
399 free( p_access->p_sys );
400 free( psz_accessname );
404 i_ret = shout_open( p_shout );
405 if( i_ret == SHOUTERR_SUCCESS )
407 i_ret = SHOUTERR_CONNECTED;
412 for non-blocking, use:
413 while( i_ret == SHOUTERR_BUSY )
416 i_ret = shout_get_connected( p_shout );
419 if( i_ret != SHOUTERR_CONNECTED )
421 msg_Err( p_access, "failed to open shout stream to %s:%i/%s: %s",
422 psz_host, i_port, psz_mount, shout_get_error(p_shout) );
423 free( p_access->p_sys );
424 free( psz_accessname );
428 p_access->pf_write = Write;
429 p_access->pf_read = Read;
430 p_access->pf_seek = Seek;
432 msg_Dbg( p_access, "shout access output opened (%s@%s:%i/%s)",
433 psz_user, psz_host, i_port, psz_mount );
435 /* Update pace control flag */
436 if( p_access->psz_access && !strcmp( p_access->psz_access, "stream" ) )
438 p_access->p_sout->i_out_pace_nocontrol++;
441 free( psz_accessname );
446 /*****************************************************************************
447 * Close: close the target
448 *****************************************************************************/
449 static void Close( vlc_object_t * p_this )
451 sout_access_out_t *p_access = (sout_access_out_t*)p_this;
453 if( p_access->p_sys && p_access->p_sys->p_shout )
455 shout_close( p_access->p_sys->p_shout );
458 free( p_access->p_sys );
460 /* Update pace control flag */
461 if( p_access->psz_access && !strcmp( p_access->psz_access, "stream" ) )
463 p_access->p_sout->i_out_pace_nocontrol--;
466 msg_Dbg( p_access, "shout access output closed" );
469 /*****************************************************************************
470 * Read: standard read -- not supported
471 *****************************************************************************/
472 static int Read( sout_access_out_t *p_access, block_t *p_buffer )
474 msg_Err( p_access, "cannot read from shout" );
478 /*****************************************************************************
479 * Write: standard write
480 *****************************************************************************/
481 static int Write( sout_access_out_t *p_access, block_t *p_buffer )
485 shout_sync( p_access->p_sys->p_shout );
488 block_t *p_next = p_buffer->p_next;
490 if( shout_send( p_access->p_sys->p_shout,
491 p_buffer->p_buffer, p_buffer->i_buffer )
492 == SHOUTERR_SUCCESS )
494 i_write += p_buffer->i_buffer;
498 msg_Err( p_access, "cannot write to stream: %s",
499 shout_get_error(p_access->p_sys->p_shout) );
501 block_Release( p_buffer );
503 /* XXX: Unsure if that's the cause for some audio trouble... */
511 /*****************************************************************************
512 * Seek: seek to a specific location -- not supported
513 *****************************************************************************/
514 static int Seek( sout_access_out_t *p_access, off_t i_pos )
516 msg_Err( p_access, "cannot seek on shout" );