1 /*****************************************************************************
2 * standard.c: standard stream output module
3 *****************************************************************************
4 * Copyright (C) 2003-2007 the VideoLAN team
7 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
34 #include <vlc_network.h>
37 /*****************************************************************************
39 *****************************************************************************/
40 #define ACCESS_TEXT N_("Output access method")
41 #define ACCESS_LONGTEXT N_( \
42 "Output method to use for the stream." )
43 #define MUX_TEXT N_("Output muxer")
44 #define MUX_LONGTEXT N_( \
45 "Muxer to use for the stream." )
46 #define DST_TEXT N_("Output destination")
47 #define DST_LONGTEXT N_( \
48 "Destination (URL) to use for the stream." )
49 #define NAME_TEXT N_("Session name")
50 #define NAME_LONGTEXT N_( \
51 "This allows you to specify a name for the session, that will be announced "\
52 "if you choose to use SAP." )
54 #define GROUP_TEXT N_("Session groupname")
55 #define GROUP_LONGTEXT N_( \
56 "This allows you to specify a group for the session, that will be announced "\
57 "if you choose to use SAP." )
59 #define DESC_TEXT N_("Session descriptipn")
60 #define DESC_LONGTEXT N_( \
61 "This allows you to give a short description with details about the stream, " \
62 "that will be announced in the SDP (Session Descriptor)." )
63 #define URL_TEXT N_("Session URL")
64 #define URL_LONGTEXT N_( \
65 "This allows you to give an URL with more details about the stream " \
66 "(often the website of the streaming organization), that will " \
67 "be announced in the SDP (Session Descriptor)." )
68 #define EMAIL_TEXT N_("Session email")
69 #define EMAIL_LONGTEXT N_( \
70 "This allows you to give a contact mail address for the stream, that will " \
71 "be announced in the SDP (Session Descriptor)." )
72 #define PHONE_TEXT N_("Session phone number")
73 #define PHONE_LONGTEXT N_( \
74 "This allows you to give a contact telephone number for the stream, that will " \
75 "be announced in the SDP (Session Descriptor)." )
78 #define SAP_TEXT N_("SAP announcing")
79 #define SAP_LONGTEXT N_("Announce this session with SAP.")
81 static int Open ( vlc_object_t
* );
82 static void Close ( vlc_object_t
* );
84 #define SOUT_CFG_PREFIX "sout-standard-"
87 set_shortname( _("Standard"));
88 set_description( _("Standard stream output") );
89 set_capability( "sout stream", 50 );
90 add_shortcut( "standard" );
91 add_shortcut( "std" );
92 set_category( CAT_SOUT
);
93 set_subcategory( SUBCAT_SOUT_STREAM
);
95 add_string( SOUT_CFG_PREFIX
"access", "", NULL
, ACCESS_TEXT
,
96 ACCESS_LONGTEXT
, VLC_FALSE
);
97 add_string( SOUT_CFG_PREFIX
"mux", "", NULL
, MUX_TEXT
,
98 MUX_LONGTEXT
, VLC_FALSE
);
99 add_string( SOUT_CFG_PREFIX
"dst", "", NULL
, DST_TEXT
,
100 DST_LONGTEXT
, VLC_FALSE
);
102 add_bool( SOUT_CFG_PREFIX
"sap", 0, NULL
, SAP_TEXT
, SAP_LONGTEXT
, VLC_TRUE
);
103 add_string( SOUT_CFG_PREFIX
"name", "", NULL
, NAME_TEXT
, NAME_LONGTEXT
,
105 add_string( SOUT_CFG_PREFIX
"group", "", NULL
, GROUP_TEXT
, GROUP_LONGTEXT
,
107 add_string( SOUT_CFG_PREFIX
"description", "", NULL
, DESC_TEXT
, DESC_LONGTEXT
,
109 add_string( SOUT_CFG_PREFIX
"url", "", NULL
, URL_TEXT
, URL_LONGTEXT
,
111 add_string( SOUT_CFG_PREFIX
"email", "", NULL
, EMAIL_TEXT
, EMAIL_LONGTEXT
,
113 add_string( SOUT_CFG_PREFIX
"phone", "", NULL
, PHONE_TEXT
, PHONE_LONGTEXT
,
115 add_suppressed_bool( SOUT_CFG_PREFIX
"sap-ipv6" );
117 set_callbacks( Open
, Close
);
121 /*****************************************************************************
122 * Exported prototypes
123 *****************************************************************************/
124 static const char *ppsz_sout_options
[] = {
125 "access", "mux", "url", "dst",
126 "sap", "name", "group", "description", "url", "email", "phone", NULL
129 #define DEFAULT_PORT 1234
131 static sout_stream_id_t
*Add ( sout_stream_t
*, es_format_t
* );
132 static int Del ( sout_stream_t
*, sout_stream_id_t
* );
133 static int Send( sout_stream_t
*, sout_stream_id_t
*, block_t
* );
135 struct sout_stream_sys_t
138 session_descriptor_t
*p_session
;
141 /*****************************************************************************
143 *****************************************************************************/
144 static int Open( vlc_object_t
*p_this
)
146 sout_stream_t
*p_stream
= (sout_stream_t
*)p_this
;
147 sout_instance_t
*p_sout
= p_stream
->p_sout
;
155 sout_access_out_t
*p_access
;
158 const char *psz_mux_byext
= NULL
;
160 config_ChainParse( p_stream
, SOUT_CFG_PREFIX
, ppsz_sout_options
,
163 var_Get( p_stream
, SOUT_CFG_PREFIX
"access", &val
);
164 psz_access
= *val
.psz_string
? val
.psz_string
: NULL
;
165 if( !*val
.psz_string
) free( val
.psz_string
);
167 var_Get( p_stream
, SOUT_CFG_PREFIX
"mux", &val
);
168 psz_mux
= *val
.psz_string
? val
.psz_string
: NULL
;
169 if( !*val
.psz_string
) free( val
.psz_string
);
172 var_Get( p_stream
, SOUT_CFG_PREFIX
"dst", &val
);
173 psz_url
= *val
.psz_string
? val
.psz_string
: NULL
;
174 if( !*val
.psz_string
) free( val
.psz_string
);
176 p_stream
->p_sys
= malloc( sizeof( sout_stream_sys_t
) );
177 p_stream
->p_sys
->p_session
= NULL
;
179 msg_Dbg( p_this
, "creating `%s/%s://%s'", psz_access
, psz_mux
, psz_url
);
181 /* ext -> muxer name */
182 if( psz_url
&& strrchr( psz_url
, '.' ) )
185 static struct { const char *ext
; const char *mux
; } exttomux
[] =
203 { "flv", "ffmpeg{mux=flv}" },
206 const char *psz_ext
= strrchr( psz_url
, '.' ) + 1;
209 msg_Dbg( p_this
, "extension is %s", psz_ext
);
210 for( i
= 0; exttomux
[i
].ext
!= NULL
; i
++ )
212 if( !strcasecmp( psz_ext
, exttomux
[i
].ext
) )
214 psz_mux_byext
= exttomux
[i
].mux
;
218 msg_Dbg( p_this
, "extension -> mux=%s", psz_mux_byext
);
221 /* We fix access/mux to valid couple */
223 if( !psz_access
&& !psz_mux
)
228 "no access _and_ no muxer, extension gives file/%s",
230 psz_access
= strdup("file");
231 psz_mux
= strdup(psz_mux_byext
);
235 msg_Err( p_stream
, "no access _and_ no muxer (fatal error)" );
240 if( psz_access
&& !psz_mux
)
242 /* access given, no mux */
243 if( !strncmp( psz_access
, "mmsh", 4 ) )
245 psz_mux
= strdup("asfh");
247 else if (!strcmp (psz_access
, "udp")
248 || !strcmp (psz_access
, "rtp") || !strcmp (psz_access
, "udplite")
249 || !strcmp (psz_access
, "tcp") || !strcmp (psz_access
, "sctp")
250 || !strcmp (psz_access
, "dccp"))
252 psz_mux
= strdup("ts");
254 else if( psz_mux_byext
)
256 psz_mux
= strdup(psz_mux_byext
);
260 msg_Err( p_stream
, "no mux specified or found by extension" );
264 else if( psz_mux
&& !psz_access
)
266 /* mux given, no access */
267 if( !strncmp( psz_mux
, "asfh", 4 ) )
269 psz_access
= strdup("mmsh");
274 psz_access
= strdup("file");
278 /* fix or warn of incompatible couple */
279 if( psz_mux
&& psz_access
)
281 if( !strncmp( psz_access
, "mmsh", 4 ) &&
282 strncmp( psz_mux
, "asfh", 4 ) )
284 char *p
= strchr( psz_mux
,'{' );
286 msg_Warn( p_stream
, "fixing to mmsh/asfh" );
289 /* -> a little memleak but ... */
290 psz_mux
= malloc( strlen( "asfh" ) + strlen( p
) + 1);
291 sprintf( psz_mux
, "asfh%s", p
);
295 psz_mux
= strdup("asfh");
298 else if( ( !strncmp( psz_access
, "rtp", 3 ) ||
299 !strncmp( psz_access
, "udp", 3 ) ) &&
300 strncmp( psz_mux
, "ts", 2 ) )
302 msg_Err( p_stream
, "for now udp and rtp are only valid with TS" );
304 else if( strncmp( psz_access
, "file", 4 ) &&
305 ( !strncmp( psz_mux
, "mov", 3 ) ||
306 !strncmp( psz_mux
, "mp4", 3 ) ) )
308 msg_Err( p_stream
, "mov and mp4 work only with file output" );
312 msg_Dbg( p_this
, "using `%s/%s://%s'", psz_access
, psz_mux
, psz_url
);
314 /* *** find and open appropriate access module *** */
315 p_access
= sout_AccessOutNew( p_sout
, psz_access
, psz_url
);
316 if( p_access
== NULL
)
318 msg_Err( p_stream
, "no suitable sout access module for `%s/%s://%s'",
319 psz_access
, psz_mux
, psz_url
);
320 if( psz_access
) free( psz_access
);
321 if( psz_mux
) free( psz_mux
);
324 msg_Dbg( p_stream
, "access opened" );
326 /* *** find and open appropriate mux module *** */
327 p_mux
= sout_MuxNew( p_sout
, psz_mux
, p_access
);
330 msg_Err( p_stream
, "no suitable sout mux module for `%s/%s://%s'",
331 psz_access
, psz_mux
, psz_url
);
333 sout_AccessOutDelete( p_access
);
334 if( psz_access
) free( psz_access
);
335 if( psz_mux
) free( psz_mux
);
338 msg_Dbg( p_stream
, "mux opened" );
340 /* *** Create the SAP Session structure *** */
341 if( var_GetBool( p_stream
, SOUT_CFG_PREFIX
"sap" ) )
343 session_descriptor_t
*p_session
;
344 announce_method_t
*p_method
= sout_SAPMethod ();
345 const int payload_type
= 33;
347 static const struct { const char *access
; const char *fmt
; } fmts
[] =
349 /* TLS/DTLS variants (none implemented): */
350 { "dtlslite", "UDPLite/TLS/RTP/AVP %d" },
351 { "dtls", "UDP/TLS/RTP/AVP %d" },
352 { "dccps", "DCCP/TLS/RTP/AVP %d" },
353 { "tls", "TCP/TLS/RTP/AVP %d" },
355 { "udplite", "UDPLite/RTP/AVP %d" },
356 { "udp", "udp mpeg" },
357 { "rtp", "RTP/AVP %d" },
358 /* Currently unsupported access outputs: */
359 { "dccp", "DCCP/RTP/AVP %d" },
360 { "tcp", "TCP/RTP/AVP %d" },
361 /* SRTP (none implemented): */
362 { "srtp", "RTP/SAVP %d" },
363 { "sudplite", "UDPLite/RTP/SAVP %d" },
364 { "sdccp", "DCCP/RTP/SAVP %d" },
365 { "stcp", "TCP/RTP/SAVP %d" },
368 const char *psz_sdp_fmt
= NULL
;
369 char *fmt
, *src
, *dst
;
372 for (unsigned i
= 0; fmts
[i
].access
!= NULL
; i
++)
373 if (strncasecmp (fmts
[i
].access
, psz_access
, strlen (fmts
[i
].access
)) == 0)
375 psz_sdp_fmt
= fmts
[i
].fmt
;
379 src
= var_GetNonEmptyString (p_access
, "src-addr");
380 dst
= var_GetNonEmptyString (p_access
, "dst-addr");
381 sport
= var_GetInteger (p_access
, "src-port");
382 dport
= var_GetInteger (p_access
, "dst-port");
384 if ((psz_sdp_fmt
== NULL
)
385 || (asprintf (&fmt
, psz_sdp_fmt
, payload_type
) == -1))
388 msg_Dbg( p_stream
, "SAP advertized format: %s", fmt
);
389 if ((fmt
== NULL
) || ((src
== NULL
) && (dst
== NULL
)))
391 msg_Err (p_access
, "SAP announces not supported for access %s",
399 p_session
= sout_AnnounceSessionCreate (VLC_OBJECT (p_stream
),
401 sout_SessionSetMedia (VLC_OBJECT (p_stream
), p_session
, fmt
,
402 src
, sport
, dst
, dport
);
403 sout_AnnounceRegister( p_sout
, p_session
, p_method
);
404 p_stream
->p_sys
->p_session
= p_session
;
405 sout_MethodRelease (p_method
);
409 p_stream
->pf_add
= Add
;
410 p_stream
->pf_del
= Del
;
411 p_stream
->pf_send
= Send
;
413 p_stream
->p_sys
->p_mux
= p_mux
;
415 if( psz_access
) free( psz_access
);
416 if( psz_mux
) free( psz_mux
);
417 if( psz_url
) free( psz_url
);
423 /*****************************************************************************
425 *****************************************************************************/
426 static void Close( vlc_object_t
* p_this
)
428 sout_stream_t
*p_stream
= (sout_stream_t
*)p_this
;
429 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
430 sout_access_out_t
*p_access
= p_sys
->p_mux
->p_access
;
432 if( p_sys
->p_session
!= NULL
)
434 sout_AnnounceUnRegister( p_stream
->p_sout
, p_sys
->p_session
);
435 sout_AnnounceSessionDestroy( p_sys
->p_session
);
439 sout_MuxDelete( p_sys
->p_mux
);
440 sout_AccessOutDelete( p_access
);
445 struct sout_stream_id_t
447 sout_input_t
*p_input
;
451 static sout_stream_id_t
* Add( sout_stream_t
*p_stream
, es_format_t
*p_fmt
)
453 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
454 sout_stream_id_t
*id
;
456 id
= malloc( sizeof( sout_stream_id_t
) );
457 if( ( id
->p_input
= sout_MuxAddStream( p_sys
->p_mux
, p_fmt
) ) == NULL
)
467 static int Del( sout_stream_t
*p_stream
, sout_stream_id_t
*id
)
469 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
471 sout_MuxDeleteStream( p_sys
->p_mux
, id
->p_input
);
478 static int Send( sout_stream_t
*p_stream
, sout_stream_id_t
*id
,
481 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
483 sout_MuxSendBuffer( p_sys
->p_mux
, id
->p_input
, p_buffer
);