Access strings (Refs:#438)
[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 VideoLAN
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
48 #include <shout/shout.h>
49
50 /*****************************************************************************
51  * Module descriptor
52  *****************************************************************************/
53 static int  Open ( vlc_object_t * );
54 static void Close( vlc_object_t * );
55
56 #define SOUT_CFG_PREFIX "sout-shout-"
57
58 #define NAME_TEXT N_("Stream name")
59 #define NAME_LONGTEXT N_("Name to give to this stream/channel on the " \
60                          "icecast server." )
61
62 #define DESCRIPTION_TEXT N_("Stream description")
63 #define DESCRIPTION_LONGTEXT N_("Description of the stream content or " \
64                                 "information about your channel." )
65
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 "\
70                         "forward MP3 streams to the icecast server." )
71
72 vlc_module_begin();
73     set_description( _("IceCAST output") );
74     set_shortname( "Shoutcast" );
75     set_capability( "sout access", 50 );
76     set_category( CAT_SOUT );
77     set_subcategory( SUBCAT_SOUT_ACO );
78     add_shortcut( "shout" );
79     add_string( SOUT_CFG_PREFIX "name", "VLC media player - Live stream", NULL,
80                 NAME_TEXT, NAME_LONGTEXT, VLC_FALSE );
81     add_string( SOUT_CFG_PREFIX "description",
82                  "Live stream from VLC media player. " \
83                 "http://www.videolan.org/vlc", NULL,
84                 DESCRIPTION_TEXT, DESCRIPTION_LONGTEXT, VLC_FALSE );
85     add_bool(   SOUT_CFG_PREFIX "mp3", VLC_FALSE, NULL,
86                 MP3_TEXT, MP3_LONGTEXT, VLC_TRUE );
87     set_callbacks( Open, Close );
88 vlc_module_end();
89
90 /*****************************************************************************
91  * Exported prototypes
92  *****************************************************************************/
93 static const char *ppsz_sout_options[] = {
94     "name", "description", "mp3", NULL
95 };
96
97
98 /*****************************************************************************
99  * Exported prototypes
100  *****************************************************************************/
101 static int Write( sout_access_out_t *, block_t * );
102 static int Seek ( sout_access_out_t *, off_t  );
103 static int Read ( sout_access_out_t *, block_t * );
104
105 struct sout_access_out_sys_t
106 {
107     shout_t *p_shout;
108 };
109
110 /*****************************************************************************
111  * Open: open the shout connection
112  *****************************************************************************/
113 static int Open( vlc_object_t *p_this )
114 {
115     sout_access_out_t *p_access = (sout_access_out_t*)p_this;
116     sout_access_out_sys_t *p_sys;
117     shout_t *p_shout;
118     long i_ret;
119     unsigned int i_port;
120     vlc_value_t val;
121
122     char *psz_accessname = NULL;
123     char *psz_parser = NULL;
124     char *psz_user = NULL;
125     char *psz_pass = NULL;
126     char *psz_host = NULL;
127     char *psz_mount = NULL;
128     char *psz_name = NULL;
129     char *psz_description = NULL;
130     char *tmp_port = NULL;
131   
132     sout_CfgParse( p_access, SOUT_CFG_PREFIX, ppsz_sout_options, p_access->p_cfg );
133
134     psz_accessname = psz_parser = strdup( p_access->psz_name );
135
136     if( !p_access->psz_name )
137     {
138         msg_Err( p_access,
139                  "please specify url=user:password@host:port/mountpoint" );
140         return VLC_EGENERIC;
141     }
142
143     /* Parse connection data user:pwd@host:port/mountpoint */
144     psz_user = psz_parser;
145     while( psz_parser[0] && psz_parser[0] != ':' ) psz_parser++;
146     if( psz_parser[0] ) { psz_parser[0] = 0; psz_parser++; }
147     psz_pass = psz_parser;
148     while( psz_parser[0] && psz_parser[0] != '@' ) psz_parser++;
149     if( psz_parser[0] ) { psz_parser[0] = 0; psz_parser++; }
150     psz_host = psz_parser;
151     while( psz_parser[0] && psz_parser[0] != ':' ) psz_parser++;
152     if( psz_parser[0] ) { psz_parser[0] = 0; psz_parser++; }
153     tmp_port = psz_parser;
154     while( psz_parser[0] && psz_parser[0] != '/' ) psz_parser++;
155     if( psz_parser[0] ) { psz_parser[0] = 0; psz_parser++; }
156     psz_mount = psz_parser;
157
158     i_port = atoi( tmp_port );
159
160     p_sys = p_access->p_sys = malloc( sizeof( sout_access_out_sys_t ) );
161     if( !p_sys )
162     {
163         msg_Err( p_access, "out of memory" );
164         free( psz_accessname );
165         return VLC_ENOMEM;
166     }
167
168     var_Get( p_access, SOUT_CFG_PREFIX "name", &val );
169     if( *val.psz_string )
170         psz_name = val.psz_string;
171     else
172         free( val.psz_string );
173
174     var_Get( p_access, SOUT_CFG_PREFIX "description", &val );
175     if( *val.psz_string )
176         psz_description = val.psz_string;
177     else
178         free( val.psz_string );
179
180     p_shout = p_sys->p_shout = shout_new();
181     if( !p_shout
182          || shout_set_host( p_shout, psz_host ) != SHOUTERR_SUCCESS
183          || shout_set_protocol( p_shout, SHOUT_PROTOCOL_HTTP )
184              != SHOUTERR_SUCCESS
185          || shout_set_port( p_shout, i_port ) != SHOUTERR_SUCCESS
186          || shout_set_password( p_shout, psz_pass ) != SHOUTERR_SUCCESS
187          || shout_set_mount( p_shout, psz_mount ) != SHOUTERR_SUCCESS
188          || shout_set_user( p_shout, psz_user ) != SHOUTERR_SUCCESS
189          || shout_set_agent( p_shout, "VLC media player " VERSION ) != SHOUTERR_SUCCESS
190          || shout_set_name( p_shout, psz_name ) != SHOUTERR_SUCCESS
191          || shout_set_description( p_shout, psz_description ) != SHOUTERR_SUCCESS 
192 //       || shout_set_nonblocking( p_shout, 1 ) != SHOUTERR_SUCCESS
193       )
194     {
195         msg_Err( p_access, "failed to initialize shout streaming to %s:%i/%s",
196                  psz_host, i_port, psz_mount );
197         free( p_access->p_sys );
198         free( psz_accessname );
199         return VLC_EGENERIC;
200     }
201
202     if( psz_name ) free( psz_name );
203     if( psz_description ) free( psz_description );
204
205     var_Get( p_access, SOUT_CFG_PREFIX "mp3", &val );
206     if( val.b_bool == VLC_TRUE )
207         i_ret = shout_set_format( p_shout, SHOUT_FORMAT_MP3 );
208     else
209         i_ret = shout_set_format( p_shout, SHOUT_FORMAT_OGG );
210
211     if( i_ret != SHOUTERR_SUCCESS )
212     {
213         msg_Err( p_access, "failed to set the shoutcast streaming format" );
214         free( p_access->p_sys );
215         free( psz_accessname );
216         return VLC_EGENERIC;
217     }
218
219     i_ret = shout_open( p_shout );
220     if( i_ret == SHOUTERR_SUCCESS )
221     {
222         i_ret = SHOUTERR_CONNECTED;
223     }
224
225 /*
226     for non-blocking, use:
227     while( i_ret == SHOUTERR_BUSY )
228     {
229         sleep( 1 );
230         i_ret = shout_get_connected( p_shout );
231     }
232 */
233     if( i_ret != SHOUTERR_CONNECTED )
234     {
235         msg_Err( p_access, "failed to open shout stream to %s:%i/%s: %s",
236                  psz_host, i_port, psz_mount, shout_get_error(p_shout) );
237         free( p_access->p_sys );
238         free( psz_accessname );
239         return VLC_EGENERIC;
240     }
241
242     p_access->pf_write = Write;
243     p_access->pf_read  = Read;
244     p_access->pf_seek  = Seek;
245
246     msg_Dbg( p_access, "shout access output opened (%s@%s:%i/%s)",
247              psz_user, psz_host, i_port, psz_mount );
248
249     /* Update pace control flag */
250     if( p_access->psz_access && !strcmp( p_access->psz_access, "stream" ) )
251     {
252         p_access->p_sout->i_out_pace_nocontrol++;
253     }
254
255     free( psz_accessname );
256
257     return VLC_SUCCESS;
258 }
259
260 /*****************************************************************************
261  * Close: close the target
262  *****************************************************************************/
263 static void Close( vlc_object_t * p_this )
264 {
265     sout_access_out_t *p_access = (sout_access_out_t*)p_this;
266
267     if( p_access->p_sys && p_access->p_sys->p_shout )
268     {
269         shout_close( p_access->p_sys->p_shout );
270         shout_shutdown();
271     }
272     free( p_access->p_sys );
273
274     /* Update pace control flag */
275     if( p_access->psz_access && !strcmp( p_access->psz_access, "stream" ) )
276     {
277         p_access->p_sout->i_out_pace_nocontrol--;
278     }
279
280     msg_Dbg( p_access, "shout access output closed" );
281 }
282
283 /*****************************************************************************
284  * Read: standard read -- not supported
285  *****************************************************************************/
286 static int Read( sout_access_out_t *p_access, block_t *p_buffer )
287 {
288     msg_Err( p_access, "cannot read from shout" );
289     return VLC_EGENERIC;
290 }
291
292 /*****************************************************************************
293  * Write: standard write
294  *****************************************************************************/
295 static int Write( sout_access_out_t *p_access, block_t *p_buffer )
296 {
297     size_t i_write = 0;
298
299     shout_sync( p_access->p_sys->p_shout );
300     while( p_buffer )
301     {
302         block_t *p_next = p_buffer->p_next;
303
304         if( shout_send( p_access->p_sys->p_shout,
305                         p_buffer->p_buffer, p_buffer->i_buffer )
306              == SHOUTERR_SUCCESS )
307         {
308             i_write += p_buffer->i_buffer;
309         }
310         else
311         {
312             msg_Err( p_access, "cannot write to stream: %s",
313                      shout_get_error(p_access->p_sys->p_shout) );
314         }
315         block_Release( p_buffer );
316
317         /* XXX: Unsure if that's the cause for some audio trouble... */
318
319         p_buffer = p_next;
320     }
321
322     return i_write;
323 }
324
325 /*****************************************************************************
326  * Seek: seek to a specific location -- not supported
327  *****************************************************************************/
328 static int Seek( sout_access_out_t *p_access, off_t i_pos )
329 {
330     msg_Err( p_access, "cannot seek on shout" );
331     return VLC_EGENERIC;
332 }
333