1 /*****************************************************************************
2 * profiles.c: Streaming profiles
3 *****************************************************************************
4 * Copyright (C) 2006 the VideoLAN team
7 * Authors: Clément Stenac <zorglub@videolan.org>
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 #include <vlc_streaming.h>
27 #define MAX_CHAIN 32768
28 #define CHAIN_APPEND( format, args... ) { \
29 memcpy( psz_temp, psz_output, MAX_CHAIN ); \
30 snprintf( psz_output, MAX_CHAIN - 1, "%s" format , psz_temp, ## args ); }
32 #define DUPM p_module->typed.p_duplicate
33 #define STDM p_module->typed.p_std
34 #define DISM p_module->typed.p_display
35 #define TRAM p_module->typed.p_transcode
37 /**********************************************************************
38 * General chain manipulation
39 **********************************************************************/
40 /** Add a new duplicate element to a streaming chain
41 * \return the new element
43 static sout_duplicate_t *streaming_ChainAddDup( sout_chain_t *p_chain )
45 DECMALLOC_NULL( p_module, sout_module_t );
46 MALLOC_NULL( DUPM, sout_duplicate_t );
47 p_module->i_type = SOUT_MOD_DUPLICATE;
49 DUPM->pp_children = NULL;
50 TAB_APPEND( p_chain->i_modules, p_chain->pp_modules, p_module );
54 /** Add a new standard element to a streaming chain
55 * \return the new element
57 static sout_std_t *streaming_ChainAddStd( sout_chain_t *p_chain,
58 const char *psz_access,
62 DECMALLOC_NULL( p_module, sout_module_t );
63 MALLOC_NULL( STDM, sout_std_t );
64 p_module->i_type = SOUT_MOD_STD;
65 STDM->psz_mux = strdup( psz_mux );
66 STDM->psz_access = strdup( psz_access );
67 STDM->psz_url = strdup( psz_url );
68 TAB_APPEND( p_chain->i_modules, p_chain->pp_modules, p_module );
72 /** Add a new display element to a streaming chain
73 * \return the new element
75 static sout_display_t *streaming_ChainAddDisplay( sout_chain_t *p_chain )
77 DECMALLOC_NULL( p_module, sout_module_t );
78 MALLOC_NULL( DISM, sout_display_t );
79 p_module->i_type = SOUT_MOD_DISPLAY;
80 TAB_APPEND( p_chain->i_modules, p_chain->pp_modules, p_module );
84 /** Add a new transcode element to a streaming chain
85 * \return the new element
87 static sout_transcode_t *streaming_ChainAddTranscode( sout_chain_t *p_chain,
88 char *psz_vcodec, char * psz_acodec, char * psz_scodec,
89 int i_vb, float f_scale, int i_ab, int i_channels,
90 vlc_bool_t b_soverlay, char *psz_additional )
92 DECMALLOC_NULL( p_module, sout_module_t );
93 MALLOC_NULL( TRAM, sout_transcode_t );
94 p_module->i_type = SOUT_MOD_TRANSCODE;
95 memset( TRAM, 0, sizeof( sout_transcode_t ) );
96 assert( !( b_soverlay && psz_scodec ) );
97 if( psz_vcodec ) TRAM->psz_vcodec = strdup( psz_vcodec );
98 if( psz_acodec ) TRAM->psz_acodec = strdup( psz_acodec );
99 if( psz_scodec ) TRAM->psz_scodec = strdup( psz_scodec );
100 TRAM->i_vb = i_vb; TRAM->i_ab = i_ab; TRAM->f_scale = f_scale;
101 TRAM->i_channels = i_channels; TRAM->b_soverlay = b_soverlay;
102 if( psz_additional ) TRAM->psz_additional = strdup( psz_additional );
103 TAB_APPEND( p_chain->i_modules, p_chain->pp_modules, p_module );
107 /** Add a new clean child chain to an existing duplicate element */
108 static void streaming_DupAddChild( sout_duplicate_t *p_dup )
111 sout_chain_t * p_child = streaming_ChainNew();
112 TAB_APPEND( p_dup->i_children, p_dup->pp_children, p_child );
115 #define DUP_OR_CHAIN p_dup ? p_dup->pp_children[p_dup->i_children-1] : p_chain
117 #define ADD_OPT( format, args... ) { \
118 char *psz_opt; asprintf( &psz_opt, format, ##args ); \
119 INSERT_ELEM( p_chain->ppsz_options, p_chain->i_options, p_chain->i_options,\
123 /** Clean up a chain (recursively if it has some children) */
124 static void streaming_ChainClean( sout_chain_t *p_chain )
127 sout_module_t *p_module;
128 if( p_chain->i_modules )
130 for( i = p_chain->i_modules -1; i >= 0 ; i-- )
132 p_module = p_chain->pp_modules[i];
133 switch( p_module->i_type )
135 case SOUT_MOD_DUPLICATE:
136 if( DUPM->i_children == 0 ) break;
137 for( j = DUPM->i_children - 1 ; j >= 0; j-- )
138 streaming_ChainClean( DUPM->pp_children[j] );
141 FREENULL( STDM->psz_url );
142 FREENULL( STDM->psz_name );
143 FREENULL( STDM->psz_group );
145 case SOUT_MOD_TRANSCODE:
146 FREENULL( TRAM->psz_vcodec );
147 FREENULL( TRAM->psz_acodec );
148 FREENULL( TRAM->psz_scodec );
149 FREENULL( TRAM->psz_venc );
150 FREENULL( TRAM->psz_aenc );
151 FREENULL( TRAM->psz_additional );
154 REMOVE_ELEM( p_chain->pp_modules, p_chain->i_modules, i );
160 /**********************************************************************
161 * Parameters handling
162 **********************************************************************/
164 #define APPLY_PSZ( element, field ) case element: \
165 streaming_ParameterApply( p_param, &p_module->field, NULL, NULL, NULL ); break;
166 #define APPLY_INT( element, field ) case element: \
167 streaming_ParameterApply( p_param, NULL, &p_module->field, NULL, NULL ); break;
168 #define APPLY_FLOAT( element, field ) case element: \
169 streaming_ParameterApply( p_param, NULL, NULL, &p_module->field, NULL ); break;
170 #define APPLY_BOOL( element, field ) case element: \
171 streaming_ParameterApply( p_param, NULL, NULL, NULL, &p_module->field ); break;
173 /** Apply the parameters for the Std module. It will copy the values from
174 * the parameters to the fields themselves
176 void streaming_StdParametersApply( sout_std_t *p_module )
179 for( i = 0 ; i< p_module->i_params; i++ )
181 sout_param_t *p_param = p_module->pp_params[i];
182 switch( p_param->i_element )
184 APPLY_PSZ( PSZ_MUX, psz_mux );
185 APPLY_PSZ( PSZ_ACCESS, psz_access );
186 APPLY_PSZ( PSZ_URL, psz_url );
187 APPLY_PSZ( PSZ_NAME, psz_name );
188 APPLY_PSZ( PSZ_GROUP, psz_group );
193 /** Apply the parameters for the Transcode module. It will copy the values from
194 * the parameters to the fields themselves
196 void streaming_TranscodeParametersApply( sout_transcode_t *p_module )
199 for( i = 0 ; i< p_module->i_params; i++ )
201 sout_param_t *p_param = p_module->pp_params[i];
202 switch( p_param->i_element )
204 APPLY_INT( I_VB, i_vb ); APPLY_INT( I_AB, i_ab );
205 APPLY_INT( I_CHANNELS, i_channels ) ;
206 APPLY_FLOAT( F_SCALE, f_scale );
207 APPLY_BOOL( B_SOVERLAY, b_soverlay );
208 APPLY_PSZ( PSZ_VC, psz_vcodec );
209 APPLY_PSZ( PSZ_AC, psz_acodec );
210 APPLY_PSZ( PSZ_SC, psz_scodec );
211 APPLY_PSZ( PSZ_VE, psz_venc ); APPLY_PSZ( PSZ_AE, psz_aenc );
216 /** Apply a single parameter
217 * \param p_param the parameter to apply
218 * \param ppsz_dest target string, if param is a string
219 * \param pi_dest target int, if param is an integer
220 * \param pf_dest target float, if param is a float
221 * \param pb_dest target bool, if param is a bool
223 void streaming_ParameterApply( sout_param_t *p_param, char **ppsz_dest,
224 int *pi_dest, float *pf_dest, vlc_bool_t *pb_dest )
226 /* Todo : Handle psz_string like formatting */
227 if( p_param->psz_string )
230 fprintf( stderr, "Unsupported !\n" );
234 switch( p_param->i_type )
236 case VLC_VAR_INTEGER:
238 *pi_dest = p_param->value.i_int;
242 *pf_dest = p_param->value.f_float;
247 *ppsz_dest = p_param->value.psz_string ?
248 strdup( p_param->value.psz_string ) :
253 *pb_dest = p_param->value.b_bool;
259 /**********************************************************************
260 * Interaction with streaming GUI descriptors
261 **********************************************************************/
262 #define DO_ENABLE_ACCESS \
263 if( !strcmp( STDM->psz_access, "file" ) )\
265 pd->b_file = VLC_TRUE; pd->psz_file = strdup( STDM->psz_url ); \
267 else if( !strcmp( STDM->psz_access, "http" ) )\
269 pd->b_http = VLC_TRUE; pd->psz_http = strdup( STDM->psz_url ); \
271 else if( !strcmp( STDM->psz_access, "mms" ) )\
273 pd->b_mms = VLC_TRUE; pd->psz_mms = strdup( STDM->psz_url ); \
275 else if( !strcmp( STDM->psz_access, "udp" ) )\
277 pd->b_udp = VLC_TRUE; pd->psz_udp = strdup( STDM->psz_url ); \
281 msg_Err( p_this, "unahandled access %s", STDM->psz_access ); \
286 * Try to convert a chain to a gui descriptor. This is only possible for
288 * \param p_this vlc object
289 * \param p_chain the source streaming chain
290 * \param pd the destination gui descriptor object
291 * \return TRUE if the conversion succeeded, false else
293 vlc_bool_t streaming_ChainToGuiDesc( vlc_object_t *p_this,
294 sout_chain_t *p_chain, sout_gui_descr_t *pd )
297 sout_module_t *p_module;
298 if( p_chain->i_modules == 0 || p_chain->i_modules > 2 ) return VLC_FALSE;
300 if( p_chain->pp_modules[0]->i_type == SOUT_MOD_TRANSCODE )
302 if( p_chain->i_modules == 1 ) return VLC_FALSE;
303 p_module = p_chain->pp_modules[0];
306 pd->b_soverlay = TRAM->b_soverlay;
307 pd->i_vb = TRAM->i_vb; pd->i_ab = TRAM->i_ab;
308 pd->i_channels = TRAM->i_channels; pd->f_scale = TRAM->f_scale;
309 if( TRAM->psz_vcodec ) pd->psz_vcodec = strdup( TRAM->psz_vcodec );
310 if( TRAM->psz_acodec ) pd->psz_acodec = strdup( TRAM->psz_acodec );
311 if( TRAM->psz_scodec ) pd->psz_scodec = strdup( TRAM->psz_scodec );
313 if( p_chain->pp_modules[i_last]->i_type == SOUT_MOD_DUPLICATE )
315 p_module = p_chain->pp_modules[i_last];
317 // Nothing allowed after duplicate. Duplicate mustn't be empty
318 if( p_chain->i_modules > i_last +1 || !DUPM->i_children )
320 for( j = 0 ; j< DUPM->i_children ; j++ )
322 sout_chain_t *p_child = DUPM->pp_children[j];
323 if( p_child->i_modules != 1 ) return VLC_FALSE;
324 p_module = p_child->pp_modules[0];
325 if( p_module->i_type == SOUT_MOD_STD )
329 else if( p_module->i_type == SOUT_MOD_DISPLAY )
330 pd->b_local = VLC_TRUE;
331 else if( p_module->i_type == SOUT_MOD_RTP )
333 msg_Err( p_this, "RTP unhandled" );
339 if( p_chain->pp_modules[i_last]->i_type == SOUT_MOD_STD )
341 p_module = p_chain->pp_modules[i_last];
344 else if( p_chain->pp_modules[i_last]->i_type == SOUT_MOD_DISPLAY )
346 pd->b_local = VLC_TRUE;
348 else if( p_chain->pp_modules[i_last]->i_type == SOUT_MOD_RTP )
350 msg_Err( p_this, "RTP unhandled" );
359 #define HANDLE_GUI_URL( type, access ) if( pd->b_##type ) { \
360 if( p_dup ) streaming_DupAddChild( p_dup ); \
361 if( pd->i_##type > 0 ) \
364 asprintf( &psz_url, "%s:%i", pd->psz_##type, pd->i_##type ); \
365 streaming_ChainAddStd( DUP_OR_CHAIN, access, pd->psz_mux,\
371 streaming_ChainAddStd( DUP_OR_CHAIN, access, pd->psz_mux,\
376 void streaming_GuiDescToChain( vlc_object_t *p_obj, sout_chain_t *p_chain,
377 sout_gui_descr_t *pd )
379 sout_duplicate_t *p_dup = NULL;
381 /* Clean up the chain */
382 streaming_ChainClean( p_chain );
385 if( pd->psz_vcodec || pd->psz_acodec || pd->psz_scodec || pd->b_soverlay )
387 fprintf( stderr, "382 vcodec %s\n", pd->psz_vcodec );
388 streaming_ChainAddTranscode( p_chain, pd->psz_vcodec, pd->psz_acodec,
389 pd->psz_scodec, pd->i_vb, pd->f_scale,
390 pd->i_ab, pd->i_channels,
391 pd->b_soverlay, NULL );
394 if( pd->b_local + pd->b_file + pd->b_http + pd->b_mms + pd->b_rtp +
397 p_dup = streaming_ChainAddDup( p_chain );
401 if( p_dup ) streaming_DupAddChild( p_dup );
402 streaming_ChainAddDisplay( DUP_OR_CHAIN );
406 if( p_dup ) streaming_DupAddChild( p_dup );
407 streaming_ChainAddStd( DUP_OR_CHAIN, "file", pd->psz_mux,
413 if( p_dup ) streaming_DupAddChild( p_dup );
417 asprintf( &psz_url, "%s:%i", pd->psz_udp, pd->i_udp );
418 p_std = streaming_ChainAddStd( DUP_OR_CHAIN, "udp",
419 pd->psz_mux, psz_url );
424 p_std = streaming_ChainAddStd( DUP_OR_CHAIN, "udp",
425 pd->psz_mux, pd->psz_udp );
427 if( pd->i_ttl ) ADD_OPT( "ttl=%i", pd->i_ttl );
430 pd->b_sap = VLC_TRUE;
431 p_std->psz_name = strdup( pd->psz_name );
432 p_std->psz_group = pd->psz_group ? strdup( pd->psz_group ) : NULL;
435 HANDLE_GUI_URL( http, "http" )
436 HANDLE_GUI_URL( mms, "mms" )
438 #undef HANDLE_GUI_URL
440 /**********************************************************************
441 * Create a sout string from a chain
442 **********************************************************************/
443 static char * ChainToPsz( sout_chain_t *p_chain, vlc_bool_t b_root )
446 char psz_output[MAX_CHAIN];
447 char psz_temp[MAX_CHAIN];
448 if( b_root ) sprintf( psz_output, "#" );
449 else psz_output[0] = '\0';
451 for( i = 0 ; i< p_chain->i_modules; i++ )
453 sout_module_t *p_module = p_chain->pp_modules[i];
454 switch( p_module->i_type )
456 case SOUT_MOD_DUPLICATE:
457 CHAIN_APPEND( "duplicate{" );
458 for( j = 0 ; j < DUPM->i_children ; j ++ )
460 char *psz_child = ChainToPsz( DUPM->pp_children[j], VLC_FALSE);
461 fprintf(stderr, "child %s\n", psz_child);
462 CHAIN_APPEND( "dst=%s", psz_child );
464 if( j != DUPM->i_children - 1 ) CHAIN_APPEND( "," );
468 case SOUT_MOD_TRANSCODE:
469 CHAIN_APPEND( "transcode{" );
470 if( TRAM->psz_vcodec )
472 CHAIN_APPEND( "vcodec=%s,vb=%i,scale=%f", TRAM->psz_vcodec,
473 TRAM->i_vb, TRAM->f_scale );
474 if( TRAM->psz_acodec || TRAM->psz_scodec || TRAM->b_soverlay )
477 if( TRAM->psz_acodec )
479 CHAIN_APPEND( "acodec=%s,ab=%i,channels=%i", TRAM->psz_acodec,
480 TRAM->i_ab, TRAM->i_channels );
481 if( TRAM->psz_scodec || TRAM->b_soverlay )
484 assert( !(TRAM->psz_scodec && TRAM->b_soverlay) );
485 if( TRAM->psz_scodec )
486 CHAIN_APPEND( "scodec=%s", TRAM->psz_scodec) ;
487 if( TRAM->b_soverlay )
488 CHAIN_APPEND( "soverlay" );
492 case SOUT_MOD_DISPLAY:
493 CHAIN_APPEND( "display" )
496 CHAIN_APPEND( "std{access=%s,url=%s,mux=%s}", STDM->psz_access,
497 STDM->psz_url, STDM->psz_mux );
499 if( i != p_chain->i_modules - 1 ) CHAIN_APPEND( ":" );
501 return strdup( psz_output );
504 char * streaming_ChainToPsz( sout_chain_t *p_chain )
506 return ChainToPsz( p_chain, VLC_TRUE );
509 /**********************************************************************
510 * Handle streaming profiles
511 **********************************************************************/
515 * List the available profiles. Fills the pp_profiles list with preinitialized
517 * \param p_this vlc object
518 * \param pi_profiles number of listed profiles
519 * \param pp_profiles array of profiles
521 void streaming_ProfilesList( vlc_object_t *p_this, int *pi_profiles,
522 streaming_profile_t **pp_profiles )
527 /** Parse a profile */
528 int streaming_ProfileParse( vlc_object_t *p_this,streaming_profile_t *p_profile,
529 const char *psz_profile )
532 DECMALLOC_ERR( p_parser, profile_parser_t );
533 assert( p_profile ); assert( psz_profile );
535 p_parser->psz_profile = strdup( psz_profile );
536 p_parser->p_profile = p_profile;
538 p_this->p_private = (void *)p_parser;
540 /* And call the module ! All work is done now */
541 p_module = module_Need( p_this, "profile parser", "", VLC_TRUE );
544 msg_Warn( p_this, "parsing profile failed" );