Make it possible to force a distro.mak to use. This is helpfull in cross-compile...
[vlc.git] / modules / access_output / shout.c
1 /*****************************************************************************
2  * shout.c: This module forwards vorbis streams to an icecast server
3  *****************************************************************************
4  * Copyright (C) 2005 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Daniel Fischer <dan at subsignal dot org>
8  *          Derk-Jan Hartman <hartman at videolan dot org>
9  *
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.
14  *
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.
19  *
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  *****************************************************************************/
24
25 /*****************************************************************************
26  * Some Comments:
27  *
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.
31  *
32  * Typical usage:
33  *
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}'
37  *
38  *****************************************************************************/
39
40 /*****************************************************************************
41  * Preamble
42  *****************************************************************************/
43 #include <string.h>
44
45 #include <vlc/vlc.h>
46 #include <vlc_sout.h>
47 #include <vlc_block.h>
48
49 #include <shout/shout.h>
50
51 /*****************************************************************************
52  * Module descriptor
53  *****************************************************************************/
54 static int  Open ( vlc_object_t * );
55 static void Close( vlc_object_t * );
56
57 #define SOUT_CFG_PREFIX "sout-shout-"
58
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." )
62
63 #define DESCRIPTION_TEXT N_("Stream description")
64 #define DESCRIPTION_LONGTEXT N_("Description of the stream content or " \
65                                 "information about your channel." )
66
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." )
72
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. */
76
77 #define GENRE_TEXT N_("Genre description")
78 #define GENRE_LONGTEXT N_("Genre of the content. " )
79
80 #define URL_TEXT N_("URL description")
81 #define URL_LONGTEXT N_("URL with information about the stream or your channel. " )
82
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 */
86
87 #define BITRATE_TEXT N_("Bitrate")
88 #define BITRATE_LONGTEXT N_("Bitrate information of the transcoded stream. " )
89
90 #define SAMPLERATE_TEXT N_("Samplerate")
91 #define SAMPLERATE_LONGTEXT N_("Samplerate information of the transcoded stream. " )
92
93 #define CHANNELS_TEXT N_("Number of channels")
94 #define CHANNELS_LONGTEXT N_("Number of channels information of the transcoded stream. " )
95
96 #define QUALITY_TEXT N_("Ogg Vorbis Quality")
97 #define QUALITY_LONGTEXT N_("Ogg Vorbis Quality information of the transcoded stream. " )
98
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." )
104
105 vlc_module_begin();
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 );
134 vlc_module_end();
135
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
142 };
143
144
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 * );
151
152 struct sout_access_out_sys_t
153 {
154     shout_t *p_shout;
155 };
156
157 /*****************************************************************************
158  * Open: open the shout connection
159  *****************************************************************************/
160 static int Open( vlc_object_t *p_this )
161 {
162     sout_access_out_t *p_access = (sout_access_out_t*)p_this;
163     sout_access_out_sys_t *p_sys;
164     shout_t *p_shout;
165     long i_ret;
166     unsigned int i_port;
167     vlc_value_t val;
168
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;
180
181     config_ChainParse( p_access, SOUT_CFG_PREFIX, ppsz_sout_options, p_access->p_cfg );
182
183     psz_accessname = psz_parser = strdup( p_access->psz_path );
184
185     if( !p_access->psz_path )
186     {
187         msg_Err( p_access,
188                  "please specify url=user:password@host:port/mountpoint" );
189         return VLC_EGENERIC;
190     }
191
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;
206
207     i_port = atoi( tmp_port );
208
209     p_sys = p_access->p_sys = malloc( sizeof( sout_access_out_sys_t ) );
210     if( !p_sys )
211     {
212         msg_Err( p_access, "out of memory" );
213         free( psz_accessname );
214         return VLC_ENOMEM;
215     }
216
217     var_Get( p_access, SOUT_CFG_PREFIX "name", &val );
218     if( *val.psz_string )
219         psz_name = val.psz_string;
220     else
221         free( val.psz_string );
222
223     var_Get( p_access, SOUT_CFG_PREFIX "description", &val );
224     if( *val.psz_string )
225         psz_description = val.psz_string;
226     else
227         free( val.psz_string );
228
229     var_Get( p_access, SOUT_CFG_PREFIX "genre", &val );
230     if( *val.psz_string )
231         psz_genre = val.psz_string;
232     else
233         free( val.psz_string );
234
235     var_Get( p_access, SOUT_CFG_PREFIX "url", &val );
236     if( *val.psz_string )
237         psz_url = val.psz_string;
238     else
239         free( val.psz_string );
240
241     p_shout = p_sys->p_shout = shout_new();
242     if( !p_shout
243          || shout_set_host( p_shout, psz_host ) != SHOUTERR_SUCCESS
244          || shout_set_protocol( p_shout, SHOUT_PROTOCOL_ICY ) != 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 */
255       )
256     {
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 );
261         return VLC_EGENERIC;
262     }
263
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 );
268
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 );
272     else
273         i_ret = shout_set_format( p_shout, SHOUT_FORMAT_OGG );
274
275     if( i_ret != SHOUTERR_SUCCESS )
276     {
277         msg_Err( p_access, "failed to set the shoutcast streaming format" );
278         free( p_access->p_sys );
279         free( psz_accessname );
280         return VLC_EGENERIC;
281     }
282
283     /* Don't force bitrate to 0 but only use when specified. This will otherwise
284        show an empty field on icecast directory listing instead of NA */
285     var_Get( p_access, SOUT_CFG_PREFIX "bitrate", &val );
286     if( *val.psz_string )
287     {
288         i_ret = shout_set_audio_info( p_shout, SHOUT_AI_BITRATE, val.psz_string );
289         if( i_ret != SHOUTERR_SUCCESS )
290         {
291             msg_Err( p_access, "failed to set the information about the bitrate" );
292             free( p_access->p_sys );
293             free( psz_accessname );
294             return VLC_EGENERIC;
295         }
296     }
297     else
298     {
299         /* Bitrate information is used for icecast/shoutcast servers directory
300            listings (sorting, stream info etc.) */
301         msg_Warn( p_access, "no bitrate information specified (required for listing " \
302                             "the server as public on the shoutcast website)" );
303         free( val.psz_string );
304     }
305
306     /* Information about samplerate, channels and quality will not be propagated
307        through the YP protocol for icecast to the public directory listing when
308        the icecast server is operating in shoutcast compatibility mode */
309
310     var_Get( p_access, SOUT_CFG_PREFIX "samplerate", &val );
311     if( *val.psz_string )
312     {
313         i_ret = shout_set_audio_info( p_shout, SHOUT_AI_SAMPLERATE, val.psz_string );
314         if( i_ret != SHOUTERR_SUCCESS )
315         {
316             msg_Err( p_access, "failed to set the information about the samplerate" );
317             free( p_access->p_sys );
318             free( psz_accessname );
319             return VLC_EGENERIC;
320         }
321     }
322     else
323         free( val.psz_string );
324
325     var_Get( p_access, SOUT_CFG_PREFIX "channels", &val );
326     if( *val.psz_string )
327     {
328         i_ret = shout_set_audio_info( p_shout, SHOUT_AI_CHANNELS, val.psz_string );
329         if( i_ret != SHOUTERR_SUCCESS )
330         {
331             msg_Err( p_access, "failed to set the information about the number of channels" );
332             free( p_access->p_sys );
333             free( psz_accessname );
334             return VLC_EGENERIC;
335         }
336     }
337     else
338         free( val.psz_string );
339
340     var_Get( p_access, SOUT_CFG_PREFIX "quality", &val );
341     if( *val.psz_string )
342     {
343         i_ret = shout_set_audio_info( p_shout, SHOUT_AI_QUALITY, val.psz_string );
344         if( i_ret != SHOUTERR_SUCCESS )
345         {
346             msg_Err( p_access, "failed to set the information about Ogg Vorbis quality" );
347             free( p_access->p_sys );
348             free( psz_accessname );
349             return VLC_EGENERIC;
350         }
351     }
352     else
353         free( val.psz_string );
354
355     var_Get( p_access, SOUT_CFG_PREFIX "public", &val );
356     if( val.b_bool == VLC_TRUE )
357     {
358         i_ret = shout_set_public( p_shout, 1 );
359         if( i_ret != SHOUTERR_SUCCESS )
360         {
361             msg_Err( p_access, "failed to set the server status setting to public" );
362             free( p_access->p_sys );
363             free( psz_accessname );
364             return VLC_EGENERIC;
365         }
366     }
367
368     /* Shoutcast using ICY protocol */
369     i_ret = shout_open( p_shout );
370     if( i_ret == SHOUTERR_SUCCESS )
371     {
372         i_ret = SHOUTERR_CONNECTED;
373         msg_Dbg( p_access, "connected using 'icy' (shoutcast) protocol" );
374     }
375     else
376     {
377         msg_Warn( p_access, "failed to connect using 'icy' (shoutcast) protocol" );
378
379         /* Shout parameters cannot be changed on an open connection */
380         i_ret = shout_close( p_shout );
381         if( i_ret == SHOUTERR_SUCCESS )
382         {
383             i_ret = SHOUTERR_UNCONNECTED;
384         }
385
386         /* IceCAST using HTTP protocol */
387         i_ret = shout_set_protocol( p_shout, SHOUT_PROTOCOL_HTTP );
388         if( i_ret != SHOUTERR_SUCCESS )
389         {
390             msg_Err( p_access, "failed to set the protocol to 'http'" );
391             free( p_access->p_sys );
392             free( psz_accessname );
393             return VLC_EGENERIC;
394         }
395
396         i_ret = shout_open( p_shout );
397         if( i_ret == SHOUTERR_SUCCESS )
398         {
399             i_ret = SHOUTERR_CONNECTED;
400             msg_Dbg( p_access, "connected using 'http' (icecast 2.x) protocol" );
401         }
402         else
403             msg_Warn( p_access, "failed to connect using 'http' (icecast 2.x) protocol " );
404     }
405
406 /*
407     for non-blocking, use:
408     while( i_ret == SHOUTERR_BUSY )
409     {
410         sleep( 1 );
411         i_ret = shout_get_connected( p_shout );
412     }
413 */
414     if( i_ret != SHOUTERR_CONNECTED )
415     {
416         msg_Err( p_access, "failed to open shout stream to %s:%i/%s: %s",
417                  psz_host, i_port, psz_mount, shout_get_error(p_shout) );
418         free( p_access->p_sys );
419         free( psz_accessname );
420         return VLC_EGENERIC;
421     }
422
423     p_access->pf_write = Write;
424     p_access->pf_read  = Read;
425     p_access->pf_seek  = Seek;
426
427     msg_Dbg( p_access, "shout access output opened (%s@%s:%i/%s)",
428              psz_user, psz_host, i_port, psz_mount );
429
430     /* Update pace control flag */
431     if( p_access->psz_access && !strcmp( p_access->psz_access, "stream" ) )
432     {
433         p_access->p_sout->i_out_pace_nocontrol++;
434     }
435
436     free( psz_accessname );
437
438     return VLC_SUCCESS;
439 }
440
441 /*****************************************************************************
442  * Close: close the target
443  *****************************************************************************/
444 static void Close( vlc_object_t * p_this )
445 {
446     sout_access_out_t *p_access = (sout_access_out_t*)p_this;
447
448     if( p_access->p_sys && p_access->p_sys->p_shout )
449     {
450         shout_close( p_access->p_sys->p_shout );
451         shout_shutdown();
452     }
453     free( p_access->p_sys );
454
455     /* Update pace control flag */
456     if( p_access->psz_access && !strcmp( p_access->psz_access, "stream" ) )
457     {
458         p_access->p_sout->i_out_pace_nocontrol--;
459     }
460
461     msg_Dbg( p_access, "shout access output closed" );
462 }
463
464 /*****************************************************************************
465  * Read: standard read -- not supported
466  *****************************************************************************/
467 static int Read( sout_access_out_t *p_access, block_t *p_buffer )
468 {
469     msg_Err( p_access, "cannot read from shout" );
470     return VLC_EGENERIC;
471 }
472
473 /*****************************************************************************
474  * Write: standard write
475  *****************************************************************************/
476 static int Write( sout_access_out_t *p_access, block_t *p_buffer )
477 {
478     size_t i_write = 0;
479
480     shout_sync( p_access->p_sys->p_shout );
481     while( p_buffer )
482     {
483         block_t *p_next = p_buffer->p_next;
484
485         if( shout_send( p_access->p_sys->p_shout,
486                         p_buffer->p_buffer, p_buffer->i_buffer )
487              == SHOUTERR_SUCCESS )
488         {
489             i_write += p_buffer->i_buffer;
490         }
491         else
492         {
493             msg_Err( p_access, "cannot write to stream: %s",
494                      shout_get_error(p_access->p_sys->p_shout) );
495         }
496         block_Release( p_buffer );
497
498         /* XXX: Unsure if that's the cause for some audio trouble... */
499
500         p_buffer = p_next;
501     }
502
503     return i_write;
504 }
505
506 /*****************************************************************************
507  * Seek: seek to a specific location -- not supported
508  *****************************************************************************/
509 static int Seek( sout_access_out_t *p_access, off_t i_pos )
510 {
511     msg_Err( p_access, "cannot seek on shout" );
512     return VLC_EGENERIC;
513 }
514