That was forgotten in [18758]
[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_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
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
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 )
288     {
289         i_ret = shout_set_audio_info( p_shout, SHOUT_AI_BITRATE, val.psz_string );
290     }
291     else
292     {
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 );
298     }
299
300     if( i_ret != SHOUTERR_SUCCESS )
301     {
302         msg_Err( p_access, "failed to set the information about the bitrate" );
303         free( p_access->p_sys );
304         free( psz_accessname );
305         return VLC_EGENERIC;
306     }
307
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 */
311
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 );
315     else
316         free( val.psz_string );
317
318     if( i_ret != SHOUTERR_SUCCESS )
319     {
320         msg_Err( p_access, "failed to set the information about the samplerate" );
321         free( p_access->p_sys );
322         free( psz_accessname );
323         return VLC_EGENERIC;
324     }
325
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 );
329     else
330         free( val.psz_string );
331
332     if( i_ret != SHOUTERR_SUCCESS )
333     {
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 );
337         return VLC_EGENERIC;
338     }
339
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 );
343     else
344         free( val.psz_string );
345
346     if( i_ret != SHOUTERR_SUCCESS )
347     {
348         msg_Err( p_access, "failed to set the information about Ogg Vorbis quality" );
349         free( p_access->p_sys );
350         free( psz_accessname );
351         return VLC_EGENERIC;
352     }
353
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 );
357
358     if( i_ret != SHOUTERR_SUCCESS )
359     {
360         msg_Err( p_access, "failed to set the server status setting to public" );
361         free( p_access->p_sys );
362         free( psz_accessname );
363         return VLC_EGENERIC;
364     }
365
366     i_ret = shout_open( p_shout );
367     if( i_ret == SHOUTERR_SUCCESS )
368     {
369         i_ret = SHOUTERR_CONNECTED;
370     }
371     else
372     {
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)" );
377
378         i_ret = shout_get_format( p_shout );
379         if( i_ret != SHOUT_FORMAT_MP3 )
380         {
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 );
385             return VLC_EGENERIC;
386         }
387
388         /* Shout parameters cannot be changed on an open connection */
389         i_ret = shout_close( p_shout );
390         if( i_ret == SHOUTERR_SUCCESS )
391         {
392             i_ret = SHOUTERR_UNCONNECTED;
393         }
394
395         i_ret = shout_set_protocol( p_shout, SHOUT_PROTOCOL_ICY );
396         if( i_ret != SHOUTERR_SUCCESS )
397         {
398             msg_Err( p_access, "failed to set the protocol to 'icy'" );
399             free( p_access->p_sys );
400             free( psz_accessname );
401             return VLC_EGENERIC;
402         }
403
404         i_ret = shout_open( p_shout );
405         if( i_ret == SHOUTERR_SUCCESS )
406         {
407             i_ret = SHOUTERR_CONNECTED;
408         }
409     }
410
411 /*
412     for non-blocking, use:
413     while( i_ret == SHOUTERR_BUSY )
414     {
415         sleep( 1 );
416         i_ret = shout_get_connected( p_shout );
417     }
418 */
419     if( i_ret != SHOUTERR_CONNECTED )
420     {
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 );
425         return VLC_EGENERIC;
426     }
427
428     p_access->pf_write = Write;
429     p_access->pf_read  = Read;
430     p_access->pf_seek  = Seek;
431
432     msg_Dbg( p_access, "shout access output opened (%s@%s:%i/%s)",
433              psz_user, psz_host, i_port, psz_mount );
434
435     /* Update pace control flag */
436     if( p_access->psz_access && !strcmp( p_access->psz_access, "stream" ) )
437     {
438         p_access->p_sout->i_out_pace_nocontrol++;
439     }
440
441     free( psz_accessname );
442
443     return VLC_SUCCESS;
444 }
445
446 /*****************************************************************************
447  * Close: close the target
448  *****************************************************************************/
449 static void Close( vlc_object_t * p_this )
450 {
451     sout_access_out_t *p_access = (sout_access_out_t*)p_this;
452
453     if( p_access->p_sys && p_access->p_sys->p_shout )
454     {
455         shout_close( p_access->p_sys->p_shout );
456         shout_shutdown();
457     }
458     free( p_access->p_sys );
459
460     /* Update pace control flag */
461     if( p_access->psz_access && !strcmp( p_access->psz_access, "stream" ) )
462     {
463         p_access->p_sout->i_out_pace_nocontrol--;
464     }
465
466     msg_Dbg( p_access, "shout access output closed" );
467 }
468
469 /*****************************************************************************
470  * Read: standard read -- not supported
471  *****************************************************************************/
472 static int Read( sout_access_out_t *p_access, block_t *p_buffer )
473 {
474     msg_Err( p_access, "cannot read from shout" );
475     return VLC_EGENERIC;
476 }
477
478 /*****************************************************************************
479  * Write: standard write
480  *****************************************************************************/
481 static int Write( sout_access_out_t *p_access, block_t *p_buffer )
482 {
483     size_t i_write = 0;
484
485     shout_sync( p_access->p_sys->p_shout );
486     while( p_buffer )
487     {
488         block_t *p_next = p_buffer->p_next;
489
490         if( shout_send( p_access->p_sys->p_shout,
491                         p_buffer->p_buffer, p_buffer->i_buffer )
492              == SHOUTERR_SUCCESS )
493         {
494             i_write += p_buffer->i_buffer;
495         }
496         else
497         {
498             msg_Err( p_access, "cannot write to stream: %s",
499                      shout_get_error(p_access->p_sys->p_shout) );
500         }
501         block_Release( p_buffer );
502
503         /* XXX: Unsure if that's the cause for some audio trouble... */
504
505         p_buffer = p_next;
506     }
507
508     return i_write;
509 }
510
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 )
515 {
516     msg_Err( p_access, "cannot seek on shout" );
517     return VLC_EGENERIC;
518 }
519