]> git.sesse.net Git - vlc/blob - modules/stream_out/standard.c
f8a87116415303a9249d865e784670cf093529b2
[vlc] / modules / stream_out / standard.c
1 /*****************************************************************************
2  * standard.c: standard stream output module
3  *****************************************************************************
4  * Copyright (C) 2003-2004 VideoLAN
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include <vlc/vlc.h>
31 #include <vlc/sout.h>
32
33 #ifdef HAVE_UNISTD_H
34 #    include <unistd.h>
35 #endif
36
37 #include "announce.h"
38 #include "network.h"
39
40 /*****************************************************************************
41  * Module descriptor
42  *****************************************************************************/
43 #define ACCESS_TEXT N_("Output access method")
44 #define ACCESS_LONGTEXT N_( \
45     "Allows you to specify the output access method used for the streaming " \
46     "output." )
47 #define MUX_TEXT N_("Output muxer")
48 #define MUX_LONGTEXT N_( \
49     "Allows you to specify the output muxer method used for the streaming " \
50     "output." )
51 #define URL_TEXT N_("Output URL")
52 #define URL_LONGTEXT N_( \
53     "Allows you to specify the output URL used for the streaming output." )
54
55 #define NAME_TEXT N_("Session name")
56 #define NAME_LONGTEXT N_( \
57     "Name of the session that will be announced with SAP or SLP" )
58
59 #define GROUP_TEXT N_("Session groupname")
60 #define GROUP_LONGTEXT N_( \
61     "Name of the group that will be announced for the session" )
62
63 #define SAP_TEXT N_("SAP announcing")
64 #define SAP_LONGTEXT N_("Announce this session with SAP")
65
66 #define SAPv6_TEXT N_("SAP IPv6 announcing")
67 #define SAPv6_LONGTEXT N_("Use IPv6 to announce this session with SAP")
68
69 #define SLP_TEXT N_("SLP announcing")
70 #define SLP_LONGTEXT N_("Announce this session with SLP")
71
72 static int      Open    ( vlc_object_t * );
73 static void     Close   ( vlc_object_t * );
74
75 #define SOUT_CFG_PREFIX "sout-standard-"
76
77 vlc_module_begin();
78     set_description( _("Standard stream output") );
79     set_capability( "sout stream", 50 );
80     add_shortcut( "standard" );
81     add_shortcut( "std" );
82
83     add_string( SOUT_CFG_PREFIX "access", "", NULL, ACCESS_TEXT,
84                 ACCESS_LONGTEXT, VLC_FALSE );
85     add_string( SOUT_CFG_PREFIX "mux", "", NULL, MUX_TEXT,
86                 MUX_LONGTEXT, VLC_FALSE );
87     add_string( SOUT_CFG_PREFIX "url", "", NULL, URL_TEXT,
88                 URL_LONGTEXT, VLC_FALSE );
89
90     add_bool( SOUT_CFG_PREFIX "sap", 0, NULL, SAP_TEXT, SAP_LONGTEXT, VLC_TRUE );
91     add_string( SOUT_CFG_PREFIX "name", "", NULL, NAME_TEXT, NAME_LONGTEXT,
92                                         VLC_TRUE );
93     add_string( SOUT_CFG_PREFIX "group", "", NULL, GROUP_TEXT, GROUP_LONGTEXT,
94                                         VLC_TRUE );
95     add_bool( SOUT_CFG_PREFIX "sap-ipv6", 0, NULL, SAPv6_TEXT, SAPv6_LONGTEXT,
96                                         VLC_TRUE );
97
98     add_bool( SOUT_CFG_PREFIX "slp", 0, NULL, SLP_TEXT, SLP_LONGTEXT, VLC_TRUE );
99
100     set_callbacks( Open, Close );
101 vlc_module_end();
102
103
104 /*****************************************************************************
105  * Exported prototypes
106  *****************************************************************************/
107 static const char *ppsz_sout_options[] = {
108     "access", "mux", "url",
109     "sap", "name", "sap-ipv6", "group",
110     "slp", NULL
111 };
112
113 #define DEFAULT_IPV6_SCOPE '8'
114 #define DEFAULT_PORT 1234
115
116 static sout_stream_id_t *Add ( sout_stream_t *, es_format_t * );
117 static int               Del ( sout_stream_t *, sout_stream_id_t * );
118 static int               Send( sout_stream_t *, sout_stream_id_t *, block_t* );
119
120 struct sout_stream_sys_t
121 {
122     sout_mux_t           *p_mux;
123     slp_session_t        *p_slp;
124     session_descriptor_t *p_session;
125 };
126
127 /*****************************************************************************
128  * Open:
129  *****************************************************************************/
130 static int Open( vlc_object_t *p_this )
131 {
132     sout_stream_t       *p_stream = (sout_stream_t*)p_this;
133     sout_instance_t     *p_sout = p_stream->p_sout;
134     slp_session_t       *p_slp = NULL;
135
136     char *psz_mux;
137     char *psz_access;
138     char *psz_url;
139
140     vlc_value_t val;
141
142     sout_access_out_t   *p_access;
143     sout_mux_t          *p_mux;
144
145     char                *psz_mux_byext = NULL;
146
147     sout_CfgParse( p_stream, SOUT_CFG_PREFIX, ppsz_sout_options,
148                    p_stream->p_cfg );
149
150     var_Get( p_stream, SOUT_CFG_PREFIX "access", &val );
151     psz_access = *val.psz_string ? val.psz_string : NULL;
152     if( val.psz_string && !*val.psz_string ) free( val.psz_string );
153
154     var_Get( p_stream, SOUT_CFG_PREFIX "mux", &val );
155     psz_mux = *val.psz_string ? val.psz_string : NULL;
156     if( val.psz_string && !*val.psz_string ) free( val.psz_string );
157
158     var_Get( p_stream, SOUT_CFG_PREFIX "url", &val );
159     psz_url = *val.psz_string ? val.psz_string : NULL;
160     if( val.psz_string && !*val.psz_string ) free( val.psz_string );
161
162     p_stream->p_sys = malloc( sizeof( sout_stream_sys_t) );
163     p_stream->p_sys->p_session = NULL;
164
165     msg_Dbg( p_this, "creating `%s/%s://%s'", psz_access, psz_mux, psz_url );
166
167     /* ext -> muxer name */
168     if( psz_url && strrchr( psz_url, '.' ) )
169     {
170         /* by extention */
171         static struct { char *ext; char *mux; } exttomux[] =
172         {
173             { "avi", "avi" },
174             { "ogg", "ogg" },
175             { "ogm", "ogg" },
176             { "mp4", "mp4" },
177             { "mov", "mov" },
178             { "moov","mov" },
179             { "asf", "asf" },
180             { "wma", "asf" },
181             { "wmv", "asf" },
182             { "trp", "ts" },
183             { "ts",  "ts" },
184             { "mpg", "ps" },
185             { "mpeg","ps" },
186             { "ps",  "ps" },
187             { "mpeg1","mpeg1" },
188             { NULL,  NULL }
189         };
190         char *psz_ext = strrchr( psz_url, '.' ) + 1;
191         int  i;
192
193         msg_Dbg( p_this, "extention is %s", psz_ext );
194         for( i = 0; exttomux[i].ext != NULL; i++ )
195         {
196             if( !strcasecmp( psz_ext, exttomux[i].ext ) )
197             {
198                 psz_mux_byext = exttomux[i].mux;
199                 break;
200             }
201         }
202         msg_Dbg( p_this, "extention -> mux=%s", psz_mux_byext );
203     }
204
205     /* We fix access/mux to valid couple */
206
207     if( !psz_access && !psz_mux )
208     {
209         if( psz_mux_byext )
210         {
211             msg_Warn( p_stream,
212                       "no access _and_ no muxer, extention gives file/%s",
213                       psz_mux_byext );
214             psz_access = strdup("file");
215             psz_mux    = strdup(psz_mux_byext);
216         }
217         else
218         {
219             msg_Err( p_stream, "no access _and_ no muxer (fatal error)" );
220             return VLC_EGENERIC;
221         }
222     }
223
224     if( psz_access && !psz_mux )
225     {
226         /* access given, no mux */
227         if( !strncmp( psz_access, "mmsh", 4 ) )
228         {
229             psz_mux = strdup("asfh");
230         }
231         else if( !strncmp( psz_access, "udp", 3 ) )
232         {
233             psz_mux = strdup("ts");
234         }
235         else if( psz_mux_byext )
236         {
237             psz_mux = strdup(psz_mux_byext);
238         }
239         else
240         {
241             msg_Err( p_stream, "no mux specified or found by extention" );
242             return VLC_EGENERIC;
243         }
244     }
245     else if( psz_mux && !psz_access )
246     {
247         /* mux given, no access */
248         if( !strncmp( psz_mux, "asfh", 4 ) )
249         {
250             psz_access = strdup("mmsh");
251         }
252         else
253         {
254             /* default file */
255             psz_access = strdup("file");
256         }
257     }
258
259     /* fix or warm of incompatible couple */
260     if( psz_mux && psz_access )
261     {
262         if( !strncmp( psz_access, "mmsh", 4 ) &&
263             strncmp( psz_mux, "asfh", 4 ) )
264         {
265             char *p = strchr( psz_mux,'{' );
266
267             msg_Warn( p_stream, "fixing to mmsh/asfh" );
268             if( p )
269             {
270                 /* -> a little memleak but ... */
271                 psz_mux = malloc( strlen( "asfh" ) + strlen( p ) + 1);
272                 sprintf( psz_mux, "asfh%s", p );
273             }
274             else
275             {
276                 psz_mux = strdup("asfh");
277             }
278         }
279         else if( ( !strncmp( psz_access, "rtp", 3 ) ||
280                    !strncmp( psz_access, "udp", 3 ) ) &&
281                  strncmp( psz_mux, "ts", 2 ) )
282         {
283             msg_Err( p_stream, "for now udp and rtp are only valid with TS" );
284         }
285         else if( strncmp( psz_access, "file", 4 ) &&
286                  ( !strncmp( psz_mux, "mov", 3 ) ||
287                    !strncmp( psz_mux, "mp4", 3 ) ) )
288         {
289             msg_Err( p_stream, "mov and mp4 work only with file output" );
290         }
291     }
292
293     msg_Dbg( p_this, "using `%s/%s://%s'", psz_access, psz_mux, psz_url );
294
295     /* *** find and open appropriate access module *** */
296     p_access = sout_AccessOutNew( p_sout, psz_access, psz_url );
297     if( p_access == NULL )
298     {
299         msg_Err( p_stream, "no suitable sout access module for `%s/%s://%s'",
300                  psz_access, psz_mux, psz_url );
301         if( psz_access ) free( psz_access );
302         if( psz_mux ) free( psz_mux );
303         return VLC_EGENERIC;
304     }
305     msg_Dbg( p_stream, "access opened" );
306
307     /* *** find and open appropriate mux module *** */
308     p_mux = sout_MuxNew( p_sout, psz_mux, p_access );
309     if( p_mux == NULL )
310     {
311         msg_Err( p_stream, "no suitable sout mux module for `%s/%s://%s'",
312                  psz_access, psz_mux, psz_url );
313
314         sout_AccessOutDelete( p_access );
315         if( psz_access ) free( psz_access );
316         if( psz_mux ) free( psz_mux );
317         return VLC_EGENERIC;
318     }
319     msg_Dbg( p_stream, "mux opened" );
320
321     /*  *** Create the SAP Session structure *** */
322     var_Get( p_stream, SOUT_CFG_PREFIX "sap", &val );
323     if( val.b_bool &&
324         ( strstr( psz_access, "udp" ) || strstr( psz_access , "rtp" ) ) )
325     {
326         session_descriptor_t *p_session = sout_AnnounceSessionCreate();
327         announce_method_t *p_method =
328             sout_AnnounceMethodCreate( METHOD_TYPE_SAP );
329         vlc_url_t url;
330
331         var_Get( p_stream, SOUT_CFG_PREFIX "name", &val );
332         if( *val.psz_string )
333         {
334             p_session->psz_name = strdup( val.psz_string );
335         }
336         else
337         {
338             p_session->psz_name = strdup( psz_url );
339         }
340         free( val.psz_string );
341
342         var_Get( p_stream, SOUT_CFG_PREFIX "group", &val );
343         if( *val.psz_string )
344         {
345             p_session->psz_group = strdup( val.psz_string );
346         }
347         free( val.psz_string );
348
349         var_Get( p_stream, SOUT_CFG_PREFIX "sap-ipv6", &val );
350         p_method->i_ip_version = val.b_bool ? 6 : 4;
351
352         /* Now, parse the URL to extract host and port */
353         vlc_UrlParse( &url, psz_url , 0);
354
355         if( url.psz_host )
356         {
357             if( url.i_port == 0 ) url.i_port = DEFAULT_PORT;
358
359             p_session->psz_uri = url.psz_host;
360             p_session->i_port = url.i_port;
361             p_session->psz_sdp = NULL;
362
363             p_session->i_ttl = config_GetInt( p_sout, "ttl" );
364             p_session->i_payload = 33;
365
366             msg_Info( p_this, "SAP Enabled");
367
368             sout_AnnounceRegister( p_sout, p_session, p_method );
369
370             /* FIXME: Free p_method */
371
372             p_stream->p_sys->p_session = p_session;
373         }
374         vlc_UrlClean( &url );
375
376         if( p_method->psz_address) free( p_method->psz_address );
377         free( p_method );
378     }
379
380     /* *** Register with slp *** */
381 #ifdef HAVE_SLP_H
382     var_Get( p_stream, SOUT_CFG_PREFIX "slp", &val );
383     if( val.b_bool &&
384         ( strstr( psz_access, "udp" ) || strstr( psz_access ,  "rtp" ) ) )
385     {
386         int i_ret;
387
388         msg_Info( p_this, "SLP Enabled");
389         var_Get( p_stream, SOUT_CFG_PREFIX "name", &val );
390         if( *val.psz_string )
391         {
392             i_ret = sout_SLPReg( p_sout, psz_url, val.psz_string );
393         }
394         else
395         {
396             i_ret = sout_SLPReg( p_sout, psz_url, psz_url );
397         }
398
399         if( i_ret )
400         {
401            msg_Warn( p_sout, "SLP Registering failed");
402         }
403         else
404         {
405             p_slp = malloc(sizeof(slp_session_t));
406             p_slp->psz_url = strdup( psz_url );
407             p_slp->psz_name =
408                 strdup( *val.psz_string ? val.psz_string : psz_url );
409         }
410         free( val.psz_string );
411     }
412 #endif
413
414     p_stream->pf_add    = Add;
415     p_stream->pf_del    = Del;
416     p_stream->pf_send   = Send;
417
418     p_stream->p_sys->p_mux = p_mux;
419     p_stream->p_sys->p_slp = p_slp;
420
421     if( psz_access ) free( psz_access );
422     if( psz_mux ) free( psz_mux );
423     if( psz_url ) free( psz_url );
424
425
426     return VLC_SUCCESS;
427 }
428
429 /*****************************************************************************
430  * Close:
431  *****************************************************************************/
432 static void Close( vlc_object_t * p_this )
433 {
434     sout_stream_t     *p_stream = (sout_stream_t*)p_this;
435     sout_stream_sys_t *p_sys    = p_stream->p_sys;
436     sout_access_out_t *p_access = p_sys->p_mux->p_access;
437
438     if( p_sys->p_session != NULL )
439     {
440         sout_AnnounceUnRegister( p_stream->p_sout, p_sys->p_session );
441         sout_AnnounceSessionDestroy( p_sys->p_session );
442     }
443
444 #ifdef HAVE_SLP_H
445     if( p_sys->p_slp )
446     {
447             sout_SLPDereg( (sout_instance_t *)p_this,
448                         p_sys->p_slp->psz_url,
449                         p_sys->p_slp->psz_name);
450             free( p_sys->p_slp);
451     }
452 #endif
453
454
455     sout_MuxDelete( p_sys->p_mux );
456     sout_AccessOutDelete( p_access );
457
458     free( p_sys );
459 }
460
461 struct sout_stream_id_t
462 {
463     sout_input_t *p_input;
464 };
465
466
467 static sout_stream_id_t * Add( sout_stream_t *p_stream, es_format_t *p_fmt )
468 {
469     sout_stream_sys_t *p_sys = p_stream->p_sys;
470     sout_stream_id_t  *id;
471
472     id = malloc( sizeof( sout_stream_id_t ) );
473     if( ( id->p_input = sout_MuxAddStream( p_sys->p_mux, p_fmt ) ) == NULL )
474     {
475         free( id );
476
477         return NULL;
478     }
479
480     return id;
481 }
482
483 static int Del( sout_stream_t *p_stream, sout_stream_id_t *id )
484 {
485     sout_stream_sys_t *p_sys = p_stream->p_sys;
486
487     sout_MuxDeleteStream( p_sys->p_mux, id->p_input );
488
489     free( id );
490
491     return VLC_SUCCESS;
492 }
493
494 static int Send( sout_stream_t *p_stream, sout_stream_id_t *id,
495                  block_t *p_buffer )
496 {
497     sout_stream_sys_t *p_sys = p_stream->p_sys;
498
499     sout_MuxSendBuffer( p_sys->p_mux, id->p_input, p_buffer );
500
501     return VLC_SUCCESS;
502 }