00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include <stdlib.h>
00028 #include <string.h>
00029
00030 #include <vlc/vlc.h>
00031 #include <vlc/sout.h>
00032
00033 #ifdef HAVE_UNISTD_H
00034 # include <unistd.h>
00035 #endif
00036
00037 #include "announce.h"
00038 #include "network.h"
00039
00040
00041
00042
00043 #define ACCESS_TEXT N_("Output access method")
00044 #define ACCESS_LONGTEXT N_( \
00045 "Allows you to specify the output access method used for the streaming " \
00046 "output." )
00047 #define MUX_TEXT N_("Output muxer")
00048 #define MUX_LONGTEXT N_( \
00049 "Allows you to specify the output muxer method used for the streaming " \
00050 "output." )
00051 #define URL_TEXT N_("Output URL (deprecated)")
00052 #define URL_LONGTEXT N_( \
00053 "Allows you to specify the output URL used for the streaming output." \
00054 "Deprecated, use dst instead." )
00055
00056 #define DST_TEXT N_("Output destination")
00057 #define DST_LONGTEXT N_( \
00058 "Allows you to specify the output destination used for the streaming output." )
00059
00060 #define NAME_TEXT N_("Session name")
00061 #define NAME_LONGTEXT N_( \
00062 "Name of the session that will be announced with SAP or SLP" )
00063
00064 #define GROUP_TEXT N_("Session groupname")
00065 #define GROUP_LONGTEXT N_( \
00066 "Name of the group that will be announced for the session" )
00067
00068 #define SAP_TEXT N_("SAP announcing")
00069 #define SAP_LONGTEXT N_("Announce this session with SAP")
00070
00071 #define SLP_TEXT N_("SLP announcing")
00072 #define SLP_LONGTEXT N_("Announce this session with SLP")
00073
00074 static int Open ( vlc_object_t * );
00075 static void Close ( vlc_object_t * );
00076
00077 #define SOUT_CFG_PREFIX "sout-standard-"
00078
00079 vlc_module_begin();
00080 set_shortname( _("Standard"));
00081 set_description( _("Standard stream output") );
00082 set_capability( "sout stream", 50 );
00083 add_shortcut( "standard" );
00084 add_shortcut( "std" );
00085 set_category( CAT_SOUT );
00086 set_subcategory( SUBCAT_SOUT_STREAM );
00087
00088 add_string( SOUT_CFG_PREFIX "access", "", NULL, ACCESS_TEXT,
00089 ACCESS_LONGTEXT, VLC_FALSE );
00090 add_string( SOUT_CFG_PREFIX "mux", "", NULL, MUX_TEXT,
00091 MUX_LONGTEXT, VLC_FALSE );
00092 add_string( SOUT_CFG_PREFIX "url", "", NULL, URL_TEXT,
00093 URL_LONGTEXT, VLC_FALSE );
00094 add_string( SOUT_CFG_PREFIX "dst", "", NULL, DST_TEXT,
00095 DST_LONGTEXT, VLC_FALSE );
00096
00097 add_bool( SOUT_CFG_PREFIX "sap", 0, NULL, SAP_TEXT, SAP_LONGTEXT, VLC_TRUE );
00098 add_string( SOUT_CFG_PREFIX "name", "", NULL, NAME_TEXT, NAME_LONGTEXT,
00099 VLC_TRUE );
00100 add_string( SOUT_CFG_PREFIX "group", "", NULL, GROUP_TEXT, GROUP_LONGTEXT,
00101 VLC_TRUE );
00102 add_suppressed_bool( SOUT_CFG_PREFIX "sap-ipv6" );
00103
00104 add_bool( SOUT_CFG_PREFIX "slp", 0, NULL, SLP_TEXT, SLP_LONGTEXT, VLC_TRUE );
00105
00106 set_callbacks( Open, Close );
00107 vlc_module_end();
00108
00109
00110
00111
00112
00113 static const char *ppsz_sout_options[] = {
00114 "access", "mux", "url", "dst",
00115 "sap", "name", "group", "slp", NULL
00116 };
00117
00118 #define DEFAULT_PORT 1234
00119
00120 static sout_stream_id_t *Add ( sout_stream_t *, es_format_t * );
00121 static int Del ( sout_stream_t *, sout_stream_id_t * );
00122 static int Send( sout_stream_t *, sout_stream_id_t *, block_t* );
00123
00124 struct sout_stream_sys_t
00125 {
00126 sout_mux_t *p_mux;
00127 slp_session_t *p_slp;
00128 session_descriptor_t *p_session;
00129 };
00130
00131
00132
00133
00134 static int Open( vlc_object_t *p_this )
00135 {
00136 sout_stream_t *p_stream = (sout_stream_t*)p_this;
00137 sout_instance_t *p_sout = p_stream->p_sout;
00138 slp_session_t *p_slp = NULL;
00139
00140 char *psz_mux;
00141 char *psz_access;
00142 char *psz_url;
00143
00144 vlc_value_t val;
00145
00146 sout_access_out_t *p_access;
00147 sout_mux_t *p_mux;
00148
00149 char *psz_mux_byext = NULL;
00150
00151 sout_CfgParse( p_stream, SOUT_CFG_PREFIX, ppsz_sout_options,
00152 p_stream->p_cfg );
00153
00154 var_Get( p_stream, SOUT_CFG_PREFIX "access", &val );
00155 psz_access = *val.psz_string ? val.psz_string : NULL;
00156 if( !*val.psz_string ) free( val.psz_string );
00157
00158 var_Get( p_stream, SOUT_CFG_PREFIX "mux", &val );
00159 psz_mux = *val.psz_string ? val.psz_string : NULL;
00160 if( !*val.psz_string ) free( val.psz_string );
00161
00162
00163 var_Get( p_stream, SOUT_CFG_PREFIX "dst", &val );
00164 psz_url = *val.psz_string ? val.psz_string : NULL;
00165 if( !*val.psz_string ) free( val.psz_string );
00166 if( !psz_url )
00167 {
00168
00169 var_Get( p_stream, SOUT_CFG_PREFIX "url", &val );
00170 psz_url = *val.psz_string ? val.psz_string : NULL;
00171 if( !*val.psz_string ) free( val.psz_string );
00172 }
00173
00174 p_stream->p_sys = malloc( sizeof( sout_stream_sys_t) );
00175 p_stream->p_sys->p_session = NULL;
00176
00177 msg_Dbg( p_this, "creating `%s/%s://%s'", psz_access, psz_mux, psz_url );
00178
00179
00180 if( psz_url && strrchr( psz_url, '.' ) )
00181 {
00182
00183 static struct { char *ext; char *mux; } exttomux[] =
00184 {
00185 { "avi", "avi" },
00186 { "ogg", "ogg" },
00187 { "ogm", "ogg" },
00188 { "mp4", "mp4" },
00189 { "mov", "mov" },
00190 { "moov","mov" },
00191 { "asf", "asf" },
00192 { "wma", "asf" },
00193 { "wmv", "asf" },
00194 { "trp", "ts" },
00195 { "ts", "ts" },
00196 { "mpg", "ps" },
00197 { "mpeg","ps" },
00198 { "ps", "ps" },
00199 { "mpeg1","mpeg1" },
00200 { "wav","wav" },
00201 { NULL, NULL }
00202 };
00203 char *psz_ext = strrchr( psz_url, '.' ) + 1;
00204 int i;
00205
00206 msg_Dbg( p_this, "extention is %s", psz_ext );
00207 for( i = 0; exttomux[i].ext != NULL; i++ )
00208 {
00209 if( !strcasecmp( psz_ext, exttomux[i].ext ) )
00210 {
00211 psz_mux_byext = exttomux[i].mux;
00212 break;
00213 }
00214 }
00215 msg_Dbg( p_this, "extention -> mux=%s", psz_mux_byext );
00216 }
00217
00218
00219
00220 if( !psz_access && !psz_mux )
00221 {
00222 if( psz_mux_byext )
00223 {
00224 msg_Warn( p_stream,
00225 "no access _and_ no muxer, extention gives file/%s",
00226 psz_mux_byext );
00227 psz_access = strdup("file");
00228 psz_mux = strdup(psz_mux_byext);
00229 }
00230 else
00231 {
00232 msg_Err( p_stream, "no access _and_ no muxer (fatal error)" );
00233 return VLC_EGENERIC;
00234 }
00235 }
00236
00237 if( psz_access && !psz_mux )
00238 {
00239
00240 if( !strncmp( psz_access, "mmsh", 4 ) )
00241 {
00242 psz_mux = strdup("asfh");
00243 }
00244 else if( !strncmp( psz_access, "udp", 3 ) ||
00245 !strncmp( psz_access, "rtp", 3 ) )
00246 {
00247 psz_mux = strdup("ts");
00248 }
00249 else if( psz_mux_byext )
00250 {
00251 psz_mux = strdup(psz_mux_byext);
00252 }
00253 else
00254 {
00255 msg_Err( p_stream, "no mux specified or found by extention" );
00256 return VLC_EGENERIC;
00257 }
00258 }
00259 else if( psz_mux && !psz_access )
00260 {
00261
00262 if( !strncmp( psz_mux, "asfh", 4 ) )
00263 {
00264 psz_access = strdup("mmsh");
00265 }
00266 else
00267 {
00268
00269 psz_access = strdup("file");
00270 }
00271 }
00272
00273
00274 if( psz_mux && psz_access )
00275 {
00276 if( !strncmp( psz_access, "mmsh", 4 ) &&
00277 strncmp( psz_mux, "asfh", 4 ) )
00278 {
00279 char *p = strchr( psz_mux,'{' );
00280
00281 msg_Warn( p_stream, "fixing to mmsh/asfh" );
00282 if( p )
00283 {
00284
00285 psz_mux = malloc( strlen( "asfh" ) + strlen( p ) + 1);
00286 sprintf( psz_mux, "asfh%s", p );
00287 }
00288 else
00289 {
00290 psz_mux = strdup("asfh");
00291 }
00292 }
00293 else if( ( !strncmp( psz_access, "rtp", 3 ) ||
00294 !strncmp( psz_access, "udp", 3 ) ) &&
00295 strncmp( psz_mux, "ts", 2 ) )
00296 {
00297 msg_Err( p_stream, "for now udp and rtp are only valid with TS" );
00298 }
00299 else if( strncmp( psz_access, "file", 4 ) &&
00300 ( !strncmp( psz_mux, "mov", 3 ) ||
00301 !strncmp( psz_mux, "mp4", 3 ) ) )
00302 {
00303 msg_Err( p_stream, "mov and mp4 work only with file output" );
00304 }
00305 }
00306
00307 msg_Dbg( p_this, "using `%s/%s://%s'", psz_access, psz_mux, psz_url );
00308
00309
00310 p_access = sout_AccessOutNew( p_sout, psz_access, psz_url );
00311 if( p_access == NULL )
00312 {
00313 msg_Err( p_stream, "no suitable sout access module for `%s/%s://%s'",
00314 psz_access, psz_mux, psz_url );
00315 if( psz_access ) free( psz_access );
00316 if( psz_mux ) free( psz_mux );
00317 return VLC_EGENERIC;
00318 }
00319 msg_Dbg( p_stream, "access opened" );
00320
00321
00322 p_mux = sout_MuxNew( p_sout, psz_mux, p_access );
00323 if( p_mux == NULL )
00324 {
00325 msg_Err( p_stream, "no suitable sout mux module for `%s/%s://%s'",
00326 psz_access, psz_mux, psz_url );
00327
00328 sout_AccessOutDelete( p_access );
00329 if( psz_access ) free( psz_access );
00330 if( psz_mux ) free( psz_mux );
00331 return VLC_EGENERIC;
00332 }
00333 msg_Dbg( p_stream, "mux opened" );
00334
00335
00336 var_Get( p_stream, SOUT_CFG_PREFIX "sap", &val );
00337 if( val.b_bool &&
00338 ( strstr( psz_access, "udp" ) || strstr( psz_access , "rtp" ) ) )
00339 {
00340 session_descriptor_t *p_session = sout_AnnounceSessionCreate();
00341 announce_method_t *p_method =
00342 sout_AnnounceMethodCreate( METHOD_TYPE_SAP );
00343 vlc_url_t url;
00344
00345 var_Get( p_stream, SOUT_CFG_PREFIX "name", &val );
00346 if( *val.psz_string )
00347 p_session->psz_name = val.psz_string;
00348 else
00349 {
00350 p_session->psz_name = strdup( psz_url );
00351 free( val.psz_string );
00352 }
00353
00354 var_Get( p_stream, SOUT_CFG_PREFIX "group", &val );
00355 if( *val.psz_string )
00356 p_session->psz_group = val.psz_string;
00357 else
00358 free( val.psz_string );
00359
00360
00361 vlc_UrlParse( &url, psz_url , 0);
00362
00363 if( url.psz_host )
00364 {
00365 if( url.i_port == 0 ) url.i_port = DEFAULT_PORT;
00366
00367 p_session->psz_uri = strdup( url.psz_host );
00368 p_session->i_port = url.i_port;
00369 p_session->psz_sdp = NULL;
00370
00371 var_Get( p_access, "sout-udp-ttl", &val );
00372 p_session->i_ttl = val.i_int ?
00373 val.i_int : config_GetInt( p_sout, "ttl" );
00374 p_session->i_payload = 33;
00375 p_session->b_rtp = strstr( psz_access, "rtp") ? 1 : 0;
00376
00377 msg_Info( p_this, "SAP Enabled");
00378
00379 sout_AnnounceRegister( p_sout, p_session, p_method );
00380 p_stream->p_sys->p_session = p_session;
00381 }
00382 vlc_UrlClean( &url );
00383
00384 free( p_method );
00385 }
00386
00387
00388 #ifdef HAVE_SLP_H
00389 var_Get( p_stream, SOUT_CFG_PREFIX "slp", &val );
00390 if( val.b_bool &&
00391 ( strstr( psz_access, "udp" ) || strstr( psz_access , "rtp" ) ) )
00392 {
00393 int i_ret;
00394
00395 msg_Info( p_this, "SLP Enabled");
00396 var_Get( p_stream, SOUT_CFG_PREFIX "name", &val );
00397 if( *val.psz_string )
00398 {
00399 i_ret = sout_SLPReg( p_sout, psz_url, val.psz_string );
00400 }
00401 else
00402 {
00403 i_ret = sout_SLPReg( p_sout, psz_url, psz_url );
00404 }
00405
00406 if( i_ret )
00407 {
00408 msg_Warn( p_sout, "SLP Registering failed");
00409 }
00410 else
00411 {
00412 p_slp = malloc(sizeof(slp_session_t));
00413 p_slp->psz_url = strdup( psz_url );
00414 p_slp->psz_name =
00415 strdup( *val.psz_string ? val.psz_string : psz_url );
00416 }
00417 free( val.psz_string );
00418 }
00419 #endif
00420
00421 p_stream->pf_add = Add;
00422 p_stream->pf_del = Del;
00423 p_stream->pf_send = Send;
00424
00425 p_stream->p_sys->p_mux = p_mux;
00426 p_stream->p_sys->p_slp = p_slp;
00427
00428 if( psz_access ) free( psz_access );
00429 if( psz_mux ) free( psz_mux );
00430 if( psz_url ) free( psz_url );
00431
00432
00433 return VLC_SUCCESS;
00434 }
00435
00436
00437
00438
00439 static void Close( vlc_object_t * p_this )
00440 {
00441 sout_stream_t *p_stream = (sout_stream_t*)p_this;
00442 sout_stream_sys_t *p_sys = p_stream->p_sys;
00443 sout_access_out_t *p_access = p_sys->p_mux->p_access;
00444
00445 if( p_sys->p_session != NULL )
00446 {
00447 sout_AnnounceUnRegister( p_stream->p_sout, p_sys->p_session );
00448 sout_AnnounceSessionDestroy( p_sys->p_session );
00449 }
00450
00451 #ifdef HAVE_SLP_H
00452 if( p_sys->p_slp )
00453 {
00454 sout_SLPDereg( (sout_instance_t *)p_this,
00455 p_sys->p_slp->psz_url,
00456 p_sys->p_slp->psz_name);
00457 free( p_sys->p_slp);
00458 }
00459 #endif
00460
00461
00462 sout_MuxDelete( p_sys->p_mux );
00463 sout_AccessOutDelete( p_access );
00464
00465 free( p_sys );
00466 }
00467
00468 struct sout_stream_id_t
00469 {
00470 sout_input_t *p_input;
00471 };
00472
00473
00474 static sout_stream_id_t * Add( sout_stream_t *p_stream, es_format_t *p_fmt )
00475 {
00476 sout_stream_sys_t *p_sys = p_stream->p_sys;
00477 sout_stream_id_t *id;
00478
00479 id = malloc( sizeof( sout_stream_id_t ) );
00480 if( ( id->p_input = sout_MuxAddStream( p_sys->p_mux, p_fmt ) ) == NULL )
00481 {
00482 free( id );
00483
00484 return NULL;
00485 }
00486
00487 return id;
00488 }
00489
00490 static int Del( sout_stream_t *p_stream, sout_stream_id_t *id )
00491 {
00492 sout_stream_sys_t *p_sys = p_stream->p_sys;
00493
00494 sout_MuxDeleteStream( p_sys->p_mux, id->p_input );
00495
00496 free( id );
00497
00498 return VLC_SUCCESS;
00499 }
00500
00501 static int Send( sout_stream_t *p_stream, sout_stream_id_t *id,
00502 block_t *p_buffer )
00503 {
00504 sout_stream_sys_t *p_sys = p_stream->p_sys;
00505
00506 sout_MuxSendBuffer( p_sys->p_mux, id->p_input, p_buffer );
00507
00508 return VLC_SUCCESS;
00509 }