]> git.sesse.net Git - vlc/blob - modules/access_output/http.c
Use separate functions for RTSP and HTTP hosts
[vlc] / modules / access_output / http.c
1 /*****************************************************************************
2  * http.c
3  *****************************************************************************
4  * Copyright (C) 2001-2009 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8  *          Jon Lech Johansen <jon@nanocrew.net>
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  * Preamble
27  *****************************************************************************/
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_sout.h>
36 #include <vlc_block.h>
37
38
39 #include <vlc_input.h>
40 #include <vlc_playlist.h>
41
42 #if 0 //def HAVE_AVAHI_CLIENT
43     #include "bonjour.h"
44
45     #if defined( WIN32 )
46         #define DIRECTORY_SEPARATOR '\\'
47     #else
48         #define DIRECTORY_SEPARATOR '/'
49     #endif
50 #endif
51
52 #include <vlc_httpd.h>
53
54 #define DEFAULT_PORT        8080
55 #define DEFAULT_SSL_PORT    8443
56
57 /*****************************************************************************
58  * Module descriptor
59  *****************************************************************************/
60 static int  Open ( vlc_object_t * );
61 static void Close( vlc_object_t * );
62
63 #define SOUT_CFG_PREFIX "sout-http-"
64
65 #define USER_TEXT N_("Username")
66 #define USER_LONGTEXT N_("User name that will be " \
67                          "requested to access the stream." )
68 #define PASS_TEXT N_("Password")
69 #define PASS_LONGTEXT N_("Password that will be " \
70                          "requested to access the stream." )
71 #define MIME_TEXT N_("Mime")
72 #define MIME_LONGTEXT N_("MIME returned by the server (autodetected " \
73                         "if not specified)." )
74 #define BONJOUR_TEXT N_( "Advertise with Bonjour")
75 #define BONJOUR_LONGTEXT N_( "Advertise the stream with the Bonjour protocol." )
76
77
78 vlc_module_begin ()
79     set_description( N_("HTTP stream output") )
80     set_capability( "sout access", 0 )
81     set_shortname( "HTTP" )
82     add_shortcut( "http", "https", "mmsh" )
83     set_category( CAT_SOUT )
84     set_subcategory( SUBCAT_SOUT_ACO )
85     add_string( SOUT_CFG_PREFIX "user", "",
86                 USER_TEXT, USER_LONGTEXT, true )
87     add_password( SOUT_CFG_PREFIX "pwd", "",
88                   PASS_TEXT, PASS_LONGTEXT, true )
89     add_string( SOUT_CFG_PREFIX "mime", "",
90                 MIME_TEXT, MIME_LONGTEXT, true )
91 #if 0 //def HAVE_AVAHI_CLIENT
92     add_bool( SOUT_CFG_PREFIX "bonjour", false,
93               BONJOUR_TEXT, BONJOUR_LONGTEXT, true);
94 #endif
95     set_callbacks( Open, Close )
96 vlc_module_end ()
97
98
99 /*****************************************************************************
100  * Exported prototypes
101  *****************************************************************************/
102 static const char *const ppsz_sout_options[] = {
103     "user", "pwd", "mime", "cert", "key", "ca", "crl", NULL
104 };
105
106 static ssize_t Write( sout_access_out_t *, block_t * );
107 static int Seek ( sout_access_out_t *, off_t  );
108 static int Control( sout_access_out_t *, int, va_list );
109
110 struct sout_access_out_sys_t
111 {
112     /* host */
113     httpd_host_t        *p_httpd_host;
114
115     /* stream */
116     httpd_stream_t      *p_httpd_stream;
117
118     /* gather header from stream */
119     int                 i_header_allocated;
120     int                 i_header_size;
121     uint8_t             *p_header;
122     bool          b_header_complete;
123
124 #if 0 //def HAVE_AVAHI_CLIENT
125     void                *p_bonjour;
126 #endif
127 };
128
129 /*****************************************************************************
130  * Open: open the file
131  *****************************************************************************/
132 static int Open( vlc_object_t *p_this )
133 {
134     sout_access_out_t       *p_access = (sout_access_out_t*)p_this;
135     sout_access_out_sys_t   *p_sys;
136
137     char                *psz_parser;
138
139     char                *psz_bind_addr;
140     int                 i_bind_port;
141     char                *psz_file_name;
142     char                *psz_user;
143     char                *psz_pwd;
144     char                *psz_mime;
145
146     if( !( p_sys = p_access->p_sys =
147                 malloc( sizeof( sout_access_out_sys_t ) ) ) )
148         return VLC_ENOMEM ;
149
150     config_ChainParse( p_access, SOUT_CFG_PREFIX, ppsz_sout_options, p_access->p_cfg );
151
152     /* p_access->psz_path = "hostname:port/filename" */
153     psz_bind_addr = strdup( p_access->psz_path );
154
155     i_bind_port = 0;
156
157     psz_parser = strchr( psz_bind_addr, '/' );
158     if( psz_parser )
159     {
160         psz_file_name = strdup( psz_parser );
161         *psz_parser = '\0';
162     }
163     else
164         psz_file_name = strdup( "/" );
165
166     if( psz_bind_addr[0] == '[' )
167     {
168         psz_bind_addr++;
169         psz_parser = strstr( psz_bind_addr, "]:" );
170         if( psz_parser )
171         {
172             *psz_parser = '\0';
173             i_bind_port = atoi( psz_parser + 2 );
174         }
175         psz_parser = psz_bind_addr - 1;
176     }
177     else
178     {
179         psz_parser = strrchr( psz_bind_addr, ':' );
180         if( psz_parser )
181         {
182             *psz_parser = '\0';
183             i_bind_port = atoi( psz_parser + 1 );
184         }
185         psz_parser = psz_bind_addr;
186     }
187
188     /* TLS support */
189     if( p_access->psz_access && !strcmp( p_access->psz_access, "https" ) )
190     {
191         if( i_bind_port <= 0 )
192             i_bind_port = DEFAULT_SSL_PORT;
193         p_sys->p_httpd_host = vlc_https_HostNew( VLC_OBJECT(p_access),
194                                                  psz_bind_addr, i_bind_port );
195     }
196     else
197     {
198         if( i_bind_port <= 0 )
199             i_bind_port = DEFAULT_PORT;
200         p_sys->p_httpd_host = vlc_http_HostNew( VLC_OBJECT(p_access),
201                                                 psz_bind_addr, i_bind_port );
202     }
203
204     if( p_sys->p_httpd_host == NULL )
205     {
206         msg_Err( p_access, "cannot listen on %s port %d",
207                  psz_bind_addr, i_bind_port );
208         free( psz_file_name );
209         free( psz_parser );
210         free( p_sys );
211         return VLC_EGENERIC;
212     }
213     free( psz_parser );
214
215     psz_user = var_GetNonEmptyString( p_access, SOUT_CFG_PREFIX "user" );
216     psz_pwd = var_GetNonEmptyString( p_access, SOUT_CFG_PREFIX "pwd" );
217     if( p_access->psz_access && !strcmp( p_access->psz_access, "mmsh" ) )
218     {
219         psz_mime = strdup( "video/x-ms-asf-stream" );
220     }
221     else
222     {
223         psz_mime = var_GetNonEmptyString( p_access, SOUT_CFG_PREFIX "mime" );
224     }
225
226     p_sys->p_httpd_stream =
227         httpd_StreamNew( p_sys->p_httpd_host, psz_file_name, psz_mime,
228                          psz_user, psz_pwd, NULL );
229     free( psz_user );
230     free( psz_pwd );
231     free( psz_mime );
232
233     if( p_sys->p_httpd_stream == NULL )
234     {
235         msg_Err( p_access, "cannot add stream %s", psz_file_name );
236         httpd_HostDelete( p_sys->p_httpd_host );
237
238         free( psz_file_name );
239         free( p_sys );
240         return VLC_EGENERIC;
241     }
242
243 #if 0 //def HAVE_AVAHI_CLIENT
244     if( var_InheritBool(p_this, SOUT_CFG_PREFIX "bonjour") )
245     {
246         char                *psz_txt, *psz_name;
247         playlist_t          *p_playlist = pl_Get( p_access );
248
249         char *psz_uri = input_item_GetURI( p_playlist->status.p_item->p_input );
250         char *psz_newuri = psz_uri;
251         psz_name = strrchr( psz_newuri, DIRECTORY_SEPARATOR );
252         if( psz_name != NULL ) psz_name++;
253         else psz_name = psz_newuri;
254
255         if( psz_file_name &&
256             asprintf( &psz_txt, "path=%s", psz_file_name ) == -1 )
257             {
258                 free( psz_uri );
259                 return VLC_ENOMEM;
260             }
261
262         p_sys->p_bonjour = bonjour_start_service( (vlc_object_t *)p_access,
263                                     strcmp( p_access->psz_access, "https" )
264                                        ? "_vlc-http._tcp" : "_vlc-https._tcp",
265                                              psz_name, i_bind_port, psz_txt );
266         free( psz_uri );
267         free( psz_txt );
268
269         if( p_sys->p_bonjour == NULL )
270             msg_Err( p_access, "unable to start requested Bonjour announce" );
271     }
272     else
273         p_sys->p_bonjour = NULL;
274 #endif
275
276     free( psz_file_name );
277
278     p_sys->i_header_allocated = 1024;
279     p_sys->i_header_size      = 0;
280     p_sys->p_header           = xmalloc( p_sys->i_header_allocated );
281     p_sys->b_header_complete  = false;
282
283     p_access->pf_write       = Write;
284     p_access->pf_seek        = Seek;
285     p_access->pf_control     = Control;
286
287     return VLC_SUCCESS;
288 }
289
290 /*****************************************************************************
291  * Close: close the target
292  *****************************************************************************/
293 static void Close( vlc_object_t * p_this )
294 {
295     sout_access_out_t       *p_access = (sout_access_out_t*)p_this;
296     sout_access_out_sys_t   *p_sys = p_access->p_sys;
297
298 #if 0 //def HAVE_AVAHI_CLIENT
299     if( p_sys->p_bonjour != NULL )
300         bonjour_stop_service( p_sys->p_bonjour );
301 #endif
302
303     httpd_StreamDelete( p_sys->p_httpd_stream );
304     httpd_HostDelete( p_sys->p_httpd_host );
305
306     free( p_sys->p_header );
307
308     msg_Dbg( p_access, "Close" );
309
310     free( p_sys );
311 }
312
313 static int Control( sout_access_out_t *p_access, int i_query, va_list args )
314 {
315     (void)p_access;
316
317     switch( i_query )
318     {
319         case ACCESS_OUT_CONTROLS_PACE:
320             *va_arg( args, bool * ) = false;
321             break;
322
323         default:
324             return VLC_EGENERIC;
325     }
326     return VLC_SUCCESS;
327 }
328
329 /*****************************************************************************
330  * Write:
331  *****************************************************************************/
332 static ssize_t Write( sout_access_out_t *p_access, block_t *p_buffer )
333 {
334     sout_access_out_sys_t *p_sys = p_access->p_sys;
335     int i_err = 0;
336     int i_len = 0;
337
338     while( p_buffer )
339     {
340         block_t *p_next;
341
342         if( p_buffer->i_flags & BLOCK_FLAG_HEADER )
343         {
344             /* gather header */
345             if( p_sys->b_header_complete )
346             {
347                 /* free previously gathered header */
348                 p_sys->i_header_size = 0;
349                 p_sys->b_header_complete = false;
350             }
351             if( (int)(p_buffer->i_buffer + p_sys->i_header_size) >
352                 p_sys->i_header_allocated )
353             {
354                 p_sys->i_header_allocated =
355                     p_buffer->i_buffer + p_sys->i_header_size + 1024;
356                 p_sys->p_header = xrealloc( p_sys->p_header,
357                                                   p_sys->i_header_allocated );
358             }
359             memcpy( &p_sys->p_header[p_sys->i_header_size],
360                     p_buffer->p_buffer,
361                     p_buffer->i_buffer );
362             p_sys->i_header_size += p_buffer->i_buffer;
363         }
364         else if( !p_sys->b_header_complete )
365         {
366             p_sys->b_header_complete = true;
367
368             httpd_StreamHeader( p_sys->p_httpd_stream, p_sys->p_header,
369                                 p_sys->i_header_size );
370         }
371
372         i_len += p_buffer->i_buffer;
373         /* send data */
374         i_err = httpd_StreamSend( p_sys->p_httpd_stream, p_buffer->p_buffer,
375                                   p_buffer->i_buffer );
376
377         p_next = p_buffer->p_next;
378         block_Release( p_buffer );
379         p_buffer = p_next;
380
381         if( i_err < 0 )
382         {
383             break;
384         }
385     }
386
387     if( i_err < 0 )
388     {
389         block_ChainRelease( p_buffer );
390     }
391
392     return( i_err < 0 ? VLC_EGENERIC : i_len );
393 }
394
395 /*****************************************************************************
396  * Seek: seek to a specific location in a file
397  *****************************************************************************/
398 static int Seek( sout_access_out_t *p_access, off_t i_pos )
399 {
400     (void)i_pos;
401     msg_Warn( p_access, "HTTP sout access cannot seek" );
402     return VLC_EGENERIC;
403 }