]> git.sesse.net Git - vlc/blob - modules/stream_out/standard.c
Strings review in stream_out (Refs:#438). Please review these once more as I changed...
[vlc] / modules / stream_out / standard.c
1 /*****************************************************************************
2  * standard.c: standard stream output module
3  *****************************************************************************
4  * Copyright (C) 2003-2004 the VideoLAN team
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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, 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 #include "network.h"
34 #include "vlc_url.h"
35
36 /*****************************************************************************
37  * Module descriptor
38  *****************************************************************************/
39 #define ACCESS_TEXT N_("Output access method")
40 #define ACCESS_LONGTEXT N_( \
41     "This is the output access method that will be used." )
42 #define MUX_TEXT N_("Output muxer")
43 #define MUX_LONGTEXT N_( \
44     "This is the muxer that will be used." )
45 #define DST_TEXT N_("Output destination")
46 #define DST_LONGTEXT N_( \
47     "This is the destination (URL) that will be used for the stream." )
48 #define NAME_TEXT N_("Session name")
49 #define NAME_LONGTEXT N_( \
50   "This allows you to specify a name for the session, that will be announced "\
51   "if you choose to use SAP." )
52
53 #define GROUP_TEXT N_("Session groupname")
54 #define GROUP_LONGTEXT N_( \
55   "This allows you to specify a group for the session, that will be announced "\
56   "if you choose to use SAP." )
57
58 #define SAP_TEXT N_("SAP announcing")
59 #define SAP_LONGTEXT N_("Announce this session with SAP.")
60
61 static int      Open    ( vlc_object_t * );
62 static void     Close   ( vlc_object_t * );
63
64 #define SOUT_CFG_PREFIX "sout-standard-"
65
66 vlc_module_begin();
67     set_shortname( _("Standard"));
68     set_description( _("Standard stream output") );
69     set_capability( "sout stream", 50 );
70     add_shortcut( "standard" );
71     add_shortcut( "std" );
72     set_category( CAT_SOUT );
73     set_subcategory( SUBCAT_SOUT_STREAM );
74
75     add_string( SOUT_CFG_PREFIX "access", "", NULL, ACCESS_TEXT,
76                 ACCESS_LONGTEXT, VLC_FALSE );
77     add_string( SOUT_CFG_PREFIX "mux", "", NULL, MUX_TEXT,
78                 MUX_LONGTEXT, VLC_FALSE );
79     add_string( SOUT_CFG_PREFIX "dst", "", NULL, DST_TEXT,
80                 DST_LONGTEXT, VLC_FALSE );
81         add_deprecated( SOUT_CFG_PREFIX "url", VLC_FALSE );
82
83     add_bool( SOUT_CFG_PREFIX "sap", 0, NULL, SAP_TEXT, SAP_LONGTEXT, VLC_TRUE );
84     add_string( SOUT_CFG_PREFIX "name", "", NULL, NAME_TEXT, NAME_LONGTEXT,
85                                         VLC_TRUE );
86     add_string( SOUT_CFG_PREFIX "group", "", NULL, GROUP_TEXT, GROUP_LONGTEXT,
87                                         VLC_TRUE );
88     add_suppressed_bool( SOUT_CFG_PREFIX "sap-ipv6" );
89
90     set_callbacks( Open, Close );
91 vlc_module_end();
92
93
94 /*****************************************************************************
95  * Exported prototypes
96  *****************************************************************************/
97 static const char *ppsz_sout_options[] = {
98     "access", "mux", "url", "dst",
99     "sap", "name", "group",  NULL
100 };
101
102 #define DEFAULT_PORT 1234
103
104 static sout_stream_id_t *Add ( sout_stream_t *, es_format_t * );
105 static int               Del ( sout_stream_t *, sout_stream_id_t * );
106 static int               Send( sout_stream_t *, sout_stream_id_t *, block_t* );
107
108 struct sout_stream_sys_t
109 {
110     sout_mux_t           *p_mux;
111     session_descriptor_t *p_session;
112 };
113
114 /*****************************************************************************
115  * Open:
116  *****************************************************************************/
117 static int Open( vlc_object_t *p_this )
118 {
119     sout_stream_t       *p_stream = (sout_stream_t*)p_this;
120     sout_instance_t     *p_sout = p_stream->p_sout;
121
122     char *psz_mux;
123     char *psz_access;
124     char *psz_url;
125
126     vlc_value_t val;
127
128     sout_access_out_t   *p_access;
129     sout_mux_t          *p_mux;
130
131     char                *psz_mux_byext = NULL;
132
133     sout_CfgParse( p_stream, SOUT_CFG_PREFIX, ppsz_sout_options,
134                    p_stream->p_cfg );
135
136     var_Get( p_stream, SOUT_CFG_PREFIX "access", &val );
137     psz_access = *val.psz_string ? val.psz_string : NULL;
138     if( !*val.psz_string ) free( val.psz_string );
139
140     var_Get( p_stream, SOUT_CFG_PREFIX "mux", &val );
141     psz_mux = *val.psz_string ? val.psz_string : NULL;
142     if( !*val.psz_string ) free( val.psz_string );
143
144
145     var_Get( p_stream, SOUT_CFG_PREFIX "dst", &val );
146     psz_url = *val.psz_string ? val.psz_string : NULL;
147     if( !*val.psz_string ) free( val.psz_string );
148
149     p_stream->p_sys = malloc( sizeof( sout_stream_sys_t) );
150     p_stream->p_sys->p_session = NULL;
151
152     msg_Dbg( p_this, "creating `%s/%s://%s'", psz_access, psz_mux, psz_url );
153
154     /* ext -> muxer name */
155     if( psz_url && strrchr( psz_url, '.' ) )
156     {
157         /* by extention */
158         static struct { char *ext; char *mux; } exttomux[] =
159         {
160             { "avi", "avi" },
161             { "ogg", "ogg" },
162             { "ogm", "ogg" },
163             { "mp4", "mp4" },
164             { "mov", "mov" },
165             { "moov","mov" },
166             { "asf", "asf" },
167             { "wma", "asf" },
168             { "wmv", "asf" },
169             { "trp", "ts" },
170             { "ts",  "ts" },
171             { "mpg", "ps" },
172             { "mpeg","ps" },
173             { "ps",  "ps" },
174             { "mpeg1","mpeg1" },
175             { "wav","wav" },
176             { NULL,  NULL }
177         };
178         char *psz_ext = strrchr( psz_url, '.' ) + 1;
179         int  i;
180
181         msg_Dbg( p_this, "extention is %s", psz_ext );
182         for( i = 0; exttomux[i].ext != NULL; i++ )
183         {
184             if( !strcasecmp( psz_ext, exttomux[i].ext ) )
185             {
186                 psz_mux_byext = exttomux[i].mux;
187                 break;
188             }
189         }
190         msg_Dbg( p_this, "extention -> mux=%s", psz_mux_byext );
191     }
192
193     /* We fix access/mux to valid couple */
194
195     if( !psz_access && !psz_mux )
196     {
197         if( psz_mux_byext )
198         {
199             msg_Warn( p_stream,
200                       "no access _and_ no muxer, extention gives file/%s",
201                       psz_mux_byext );
202             psz_access = strdup("file");
203             psz_mux    = strdup(psz_mux_byext);
204         }
205         else
206         {
207             msg_Err( p_stream, "no access _and_ no muxer (fatal error)" );
208             return VLC_EGENERIC;
209         }
210     }
211
212     if( psz_access && !psz_mux )
213     {
214         /* access given, no mux */
215         if( !strncmp( psz_access, "mmsh", 4 ) )
216         {
217             psz_mux = strdup("asfh");
218         }
219         else if( !strncmp( psz_access, "udp", 3 ) ||
220                  !strncmp( psz_access, "rtp", 3 ) )
221         {
222             psz_mux = strdup("ts");
223         }
224         else if( psz_mux_byext )
225         {
226             psz_mux = strdup(psz_mux_byext);
227         }
228         else
229         {
230             msg_Err( p_stream, "no mux specified or found by extention" );
231             return VLC_EGENERIC;
232         }
233     }
234     else if( psz_mux && !psz_access )
235     {
236         /* mux given, no access */
237         if( !strncmp( psz_mux, "asfh", 4 ) )
238         {
239             psz_access = strdup("mmsh");
240         }
241         else
242         {
243             /* default file */
244             psz_access = strdup("file");
245         }
246     }
247
248     /* fix or warn of incompatible couple */
249     if( psz_mux && psz_access )
250     {
251         if( !strncmp( psz_access, "mmsh", 4 ) &&
252             strncmp( psz_mux, "asfh", 4 ) )
253         {
254             char *p = strchr( psz_mux,'{' );
255
256             msg_Warn( p_stream, "fixing to mmsh/asfh" );
257             if( p )
258             {
259                 /* -> a little memleak but ... */
260                 psz_mux = malloc( strlen( "asfh" ) + strlen( p ) + 1);
261                 sprintf( psz_mux, "asfh%s", p );
262             }
263             else
264             {
265                 psz_mux = strdup("asfh");
266             }
267         }
268         else if( ( !strncmp( psz_access, "rtp", 3 ) ||
269                    !strncmp( psz_access, "udp", 3 ) ) &&
270                  strncmp( psz_mux, "ts", 2 ) )
271         {
272             msg_Err( p_stream, "for now udp and rtp are only valid with TS" );
273         }
274         else if( strncmp( psz_access, "file", 4 ) &&
275                  ( !strncmp( psz_mux, "mov", 3 ) ||
276                    !strncmp( psz_mux, "mp4", 3 ) ) )
277         {
278             msg_Err( p_stream, "mov and mp4 work only with file output" );
279         }
280     }
281
282     msg_Dbg( p_this, "using `%s/%s://%s'", psz_access, psz_mux, psz_url );
283
284     /* *** find and open appropriate access module *** */
285     p_access = sout_AccessOutNew( p_sout, psz_access, psz_url );
286     if( p_access == NULL )
287     {
288         msg_Err( p_stream, "no suitable sout access module for `%s/%s://%s'",
289                  psz_access, psz_mux, psz_url );
290         if( psz_access ) free( psz_access );
291         if( psz_mux ) free( psz_mux );
292         return VLC_EGENERIC;
293     }
294     msg_Dbg( p_stream, "access opened" );
295
296     /* *** find and open appropriate mux module *** */
297     p_mux = sout_MuxNew( p_sout, psz_mux, p_access );
298     if( p_mux == NULL )
299     {
300         msg_Err( p_stream, "no suitable sout mux module for `%s/%s://%s'",
301                  psz_access, psz_mux, psz_url );
302
303         sout_AccessOutDelete( p_access );
304         if( psz_access ) free( psz_access );
305         if( psz_mux ) free( psz_mux );
306         return VLC_EGENERIC;
307     }
308     msg_Dbg( p_stream, "mux opened" );
309
310     /*  *** Create the SAP Session structure *** */
311     var_Get( p_stream, SOUT_CFG_PREFIX "sap", &val );
312     if( val.b_bool &&
313         ( strstr( psz_access, "udp" ) || strstr( psz_access , "rtp" ) ) )
314     {
315         session_descriptor_t *p_session = sout_AnnounceSessionCreate();
316         announce_method_t *p_method =
317             sout_AnnounceMethodCreate( METHOD_TYPE_SAP );
318         vlc_url_t url;
319
320         var_Get( p_stream, SOUT_CFG_PREFIX "name", &val );
321         if( *val.psz_string )
322             p_session->psz_name = val.psz_string;
323         else
324         {
325             p_session->psz_name = strdup( psz_url );
326             free( val.psz_string );
327         }
328
329         var_Get( p_stream, SOUT_CFG_PREFIX "group", &val );
330         if( *val.psz_string )
331             p_session->psz_group = val.psz_string;
332         else
333             free( val.psz_string );
334
335         /* Now, parse the URL to extract host and port */
336         vlc_UrlParse( &url, psz_url , 0);
337
338         if( url.psz_host )
339         {
340             if( url.i_port == 0 ) url.i_port = DEFAULT_PORT;
341
342             p_session->psz_uri = strdup( url.psz_host );
343             p_session->i_port = url.i_port;
344             p_session->psz_sdp = NULL;
345
346             var_Get( p_access, "sout-udp-ttl", &val );
347             p_session->i_ttl = val.i_int;
348             p_session->i_payload = 33;
349             p_session->b_rtp = strstr( psz_access, "rtp") ? 1 : 0;
350
351             msg_Info( p_this, "SAP Enabled");
352
353             sout_AnnounceRegister( p_sout, p_session, p_method );
354             p_stream->p_sys->p_session = p_session;
355         }
356         vlc_UrlClean( &url );
357
358         free( p_method );
359     }
360
361     p_stream->pf_add    = Add;
362     p_stream->pf_del    = Del;
363     p_stream->pf_send   = Send;
364
365     p_stream->p_sys->p_mux = p_mux;
366
367     if( psz_access ) free( psz_access );
368     if( psz_mux ) free( psz_mux );
369     if( psz_url ) free( psz_url );
370
371
372     return VLC_SUCCESS;
373 }
374
375 /*****************************************************************************
376  * Close:
377  *****************************************************************************/
378 static void Close( vlc_object_t * p_this )
379 {
380     sout_stream_t     *p_stream = (sout_stream_t*)p_this;
381     sout_stream_sys_t *p_sys    = p_stream->p_sys;
382     sout_access_out_t *p_access = p_sys->p_mux->p_access;
383
384     if( p_sys->p_session != NULL )
385     {
386         sout_AnnounceUnRegister( p_stream->p_sout, p_sys->p_session );
387         sout_AnnounceSessionDestroy( p_sys->p_session );
388     }
389
390
391     sout_MuxDelete( p_sys->p_mux );
392     sout_AccessOutDelete( p_access );
393
394     free( p_sys );
395 }
396
397 struct sout_stream_id_t
398 {
399     sout_input_t *p_input;
400 };
401
402
403 static sout_stream_id_t * Add( sout_stream_t *p_stream, es_format_t *p_fmt )
404 {
405     sout_stream_sys_t *p_sys = p_stream->p_sys;
406     sout_stream_id_t  *id;
407
408     id = malloc( sizeof( sout_stream_id_t ) );
409     if( ( id->p_input = sout_MuxAddStream( p_sys->p_mux, p_fmt ) ) == NULL )
410     {
411         free( id );
412
413         return NULL;
414     }
415
416     return id;
417 }
418
419 static int Del( sout_stream_t *p_stream, sout_stream_id_t *id )
420 {
421     sout_stream_sys_t *p_sys = p_stream->p_sys;
422
423     sout_MuxDeleteStream( p_sys->p_mux, id->p_input );
424
425     free( id );
426
427     return VLC_SUCCESS;
428 }
429
430 static int Send( sout_stream_t *p_stream, sout_stream_id_t *id,
431                  block_t *p_buffer )
432 {
433     sout_stream_sys_t *p_sys = p_stream->p_sys;
434
435     sout_MuxSendBuffer( p_sys->p_mux, id->p_input, p_buffer );
436
437     return VLC_SUCCESS;
438 }