]> git.sesse.net Git - vlc/blob - src/stream_output/profiles.c
Better compile fix by Pierre d'Herbemont
[vlc] / src / stream_output / profiles.c
1 /*****************************************************************************
2  * profiles.c: Streaming profiles
3  *****************************************************************************
4  * Copyright (C) 2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: ClĂ©ment Stenac <zorglub@videolan.org>
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 #include <vlc_streaming.h>
25 #include <assert.h>
26
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 ); }
31
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
36
37 /**********************************************************************
38  * General chain manipulation
39  **********************************************************************/
40 /** Add a new duplicate element to a streaming chain
41  * \return the new element
42  */
43 static sout_duplicate_t *streaming_ChainAddDup( sout_chain_t *p_chain )
44 {
45     DECMALLOC_NULL( p_module, sout_module_t );
46     MALLOC_NULL( DUPM, sout_duplicate_t );
47     p_module->i_type = SOUT_MOD_DUPLICATE;
48     DUPM->i_children = 0;
49     DUPM->pp_children = NULL;
50     TAB_APPEND( p_chain->i_modules, p_chain->pp_modules, p_module );
51     return DUPM;
52 }
53
54 /** Add a new standard element to a streaming chain
55  * \return the new element
56  */
57 static sout_std_t *streaming_ChainAddStd( sout_chain_t *p_chain,
58                                           const char *psz_access,
59                                           const char *psz_mux,
60                                           const char *psz_url )
61 {
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 );
69     return STDM;
70 }
71
72 /** Add a new display element to a streaming chain
73  * \return the new element
74  */
75 static sout_display_t *streaming_ChainAddDisplay( sout_chain_t *p_chain )
76 {
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 );
81     return DISM;
82 }
83
84 /** Add a new transcode element to a streaming chain
85  * \return the new element
86  */
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 )
91 {
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 );
104     return TRAM;
105 }
106
107 /** Add a new clean child chain to an existing duplicate element */
108 static void streaming_DupAddChild( sout_duplicate_t *p_dup )
109 {
110     assert( p_dup );
111     sout_chain_t * p_child = streaming_ChainNew();
112     TAB_APPEND( p_dup->i_children, p_dup->pp_children, p_child );
113 }
114
115 #define DUP_OR_CHAIN p_dup ? p_dup->pp_children[p_dup->i_children-1] : p_chain
116
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,\
120                  psz_opt );\
121     free( psz_opt ); }
122
123 /** Clean up a chain (recursively if it has some children) */
124 static void streaming_ChainClean( sout_chain_t *p_chain )
125 {
126     int i,j;
127     sout_module_t *p_module;
128     if( p_chain->i_modules )
129     {
130         for( i = p_chain->i_modules -1; i >= 0 ; i-- )
131         {
132             p_module = p_chain->pp_modules[i];
133             switch( p_module->i_type )
134             {
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] );
139                 break;
140             case SOUT_MOD_STD:
141                 FREENULL( STDM->psz_url );
142                 FREENULL( STDM->psz_name );
143                 FREENULL( STDM->psz_group );
144                 break;
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 );
152                 break;
153             }
154             REMOVE_ELEM( p_chain->pp_modules, p_chain->i_modules, i );
155             free( p_module );
156         }
157     }
158 }
159
160 /**********************************************************************
161  * Parameters handling
162  **********************************************************************/
163
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;
172
173 /** Apply the parameters for the Std module. It will copy the values from
174  * the parameters to the fields themselves
175  */
176 void streaming_StdParametersApply( sout_std_t *p_module )
177 {
178     int i;
179     for( i = 0 ; i< p_module->i_params; i++ )
180     {
181         sout_param_t *p_param = p_module->pp_params[i];
182         switch( p_param->i_element )
183         {
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 );
189         }
190     }
191 }
192
193 /** Apply the parameters for the Transcode module. It will copy the values from
194  * the parameters to the fields themselves
195  */
196 void streaming_TranscodeParametersApply( sout_transcode_t *p_module )
197 {
198     int i;
199     for( i = 0 ; i< p_module->i_params; i++ )
200     {
201         sout_param_t *p_param = p_module->pp_params[i];
202         switch( p_param->i_element )
203         {
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 );
212         }
213     }
214 }
215
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
222  */
223 void streaming_ParameterApply( sout_param_t *p_param, char **ppsz_dest,
224                              int *pi_dest, float *pf_dest, vlc_bool_t *pb_dest )
225 {
226     /* Todo : Handle psz_string like formatting */
227     if( p_param->psz_string )
228     {
229         assert( ppsz_dest );
230         fprintf( stderr, "Unsupported !\n" );
231     }
232     else
233     {
234         switch( p_param->i_type )
235         {
236         case VLC_VAR_INTEGER:
237             assert( pi_dest );
238             *pi_dest = p_param->value.i_int;
239             break;
240         case VLC_VAR_FLOAT:
241             assert( pf_dest );
242             *pf_dest = p_param->value.f_float;
243             break;
244         case VLC_VAR_STRING:
245             assert( ppsz_dest );
246             free( *ppsz_dest );
247             *ppsz_dest = p_param->value.psz_string ?
248                                 strdup( p_param->value.psz_string ) :
249                                 NULL;
250             break;
251         case VLC_VAR_BOOL:
252             assert( pb_dest );
253             *pb_dest = p_param->value.b_bool;
254             break;
255         }
256     }
257 }
258
259 /**********************************************************************
260  * Interaction with streaming GUI descriptors
261  **********************************************************************/
262 #define DO_ENABLE_ACCESS \
263     if( !strcmp( STDM->psz_access, "file" ) )\
264     { \
265         pd->b_file = VLC_TRUE; pd->psz_file = strdup( STDM->psz_url ); \
266     } \
267     else if(  !strcmp( STDM->psz_access, "http" ) )\
268     { \
269         pd->b_http = VLC_TRUE; pd->psz_http = strdup( STDM->psz_url ); \
270     } \
271     else if(  !strcmp( STDM->psz_access, "mms" ) )\
272     { \
273         pd->b_mms = VLC_TRUE; pd->psz_mms = strdup( STDM->psz_url ); \
274     } \
275     else if(  !strcmp( STDM->psz_access, "udp" ) )\
276     { \
277         pd->b_udp = VLC_TRUE; pd->psz_udp = strdup( STDM->psz_url ); \
278     } \
279     else \
280     { \
281         msg_Err( p_this, "unahandled access %s", STDM->psz_access ); \
282     }
283
284 #if 0
285 /**
286  * Try to convert a chain to a gui descriptor. This is only possible for
287  * "simple" chains.
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
292  */
293 vlc_bool_t streaming_ChainToGuiDesc( vlc_object_t *p_this,
294                                   sout_chain_t *p_chain, sout_gui_descr_t *pd )
295 {
296     int j, i_last = 0;
297     sout_module_t *p_module;
298     if( p_chain->i_modules == 0 || p_chain->i_modules > 2 ) return VLC_FALSE;
299
300     if( p_chain->pp_modules[0]->i_type == SOUT_MOD_TRANSCODE )
301     {
302         if( p_chain->i_modules == 1 ) return VLC_FALSE;
303         p_module = p_chain->pp_modules[0];
304         i_last++;
305
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 );
312     }
313     if( p_chain->pp_modules[i_last]->i_type == SOUT_MOD_DUPLICATE )
314     {
315         p_module = p_chain->pp_modules[i_last];
316
317         // Nothing allowed after duplicate. Duplicate mustn't be empty
318         if( p_chain->i_modules > i_last +1 || !DUPM->i_children )
319             return VLC_FALSE;
320         for( j = 0 ; j<  DUPM->i_children ; j++ )
321         {
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 )
326             {
327                 DO_ENABLE_ACCESS
328             }
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 )
332             {
333                 msg_Err( p_this, "RTP unhandled" );
334                 return VLC_FALSE;
335             }
336         }
337         i_last++;
338     }
339     if( p_chain->pp_modules[i_last]->i_type == SOUT_MOD_STD )
340     {
341         p_module = p_chain->pp_modules[i_last];
342         DO_ENABLE_ACCESS;
343     }
344     else if( p_chain->pp_modules[i_last]->i_type == SOUT_MOD_DISPLAY )
345     {
346         pd->b_local = VLC_TRUE;
347     }
348     else if( p_chain->pp_modules[i_last]->i_type == SOUT_MOD_RTP )
349     {
350         msg_Err( p_this, "RTP unhandled" );
351         return VLC_FALSE;
352
353     }
354     return VLC_TRUE;
355
356 }
357 #endif
358
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 ) \
362         { \
363             char *psz_url; \
364             asprintf( &psz_url, "%s:%i", pd->psz_##type, pd->i_##type ); \
365             streaming_ChainAddStd( DUP_OR_CHAIN, access, pd->psz_mux,\
366                                    psz_url ); \
367             free( psz_url ); \
368         } \
369         else \
370         { \
371             streaming_ChainAddStd( DUP_OR_CHAIN, access, pd->psz_mux,\
372                                    pd->psz_##type );\
373         }\
374     }
375
376 void streaming_GuiDescToChain( vlc_object_t *p_obj, sout_chain_t *p_chain,
377                                sout_gui_descr_t *pd )
378 {
379     sout_duplicate_t *p_dup = NULL;
380     /* Clean up the chain */
381     streaming_ChainClean( p_chain );
382
383     /* Transcode */
384     if( pd->psz_vcodec || pd->psz_acodec || pd->psz_scodec || pd->b_soverlay )
385     {
386         fprintf( stderr, "382 vcodec %s\n", pd->psz_vcodec );
387         streaming_ChainAddTranscode( p_chain, pd->psz_vcodec, pd->psz_acodec,
388                                      pd->psz_scodec, pd->i_vb, pd->f_scale,
389                                      pd->i_ab, pd->i_channels,
390                                      pd->b_soverlay, NULL );
391     }
392     /* #std{} */
393     if( pd->b_local + pd->b_file + pd->b_http + pd->b_mms + pd->b_rtp +
394         pd->b_udp > 1 )
395     {
396         p_dup = streaming_ChainAddDup( p_chain );
397     }
398     if( pd->b_local )
399     {
400         if( p_dup ) streaming_DupAddChild( p_dup );
401         streaming_ChainAddDisplay( DUP_OR_CHAIN );
402     }
403     if( pd->b_file )
404     {
405         if( p_dup ) streaming_DupAddChild( p_dup );
406         streaming_ChainAddStd( DUP_OR_CHAIN, "file", pd->psz_mux,
407                                pd->psz_file );
408     }
409     if( pd->b_udp )
410     {
411         sout_std_t *p_std;
412         if( p_dup ) streaming_DupAddChild( p_dup );
413         if( pd->i_udp > 0 )
414         {
415             char *psz_url;
416             asprintf( &psz_url, "%s:%i", pd->psz_udp, pd->i_udp );
417             p_std = streaming_ChainAddStd( DUP_OR_CHAIN, "udp",
418                                            pd->psz_mux, psz_url );
419             free( psz_url );
420         }
421         else
422         {
423             p_std = streaming_ChainAddStd( DUP_OR_CHAIN, "udp",
424                                            pd->psz_mux, pd->psz_udp );
425         }
426         if( pd->i_ttl ) ADD_OPT( "ttl=%i", pd->i_ttl );
427         if( pd->b_sap )
428         {
429             pd->b_sap = VLC_TRUE;
430             p_std->psz_name = strdup( pd->psz_name );
431             p_std->psz_group = pd->psz_group ? strdup( pd->psz_group ) : NULL;
432         }
433     }
434     HANDLE_GUI_URL( http, "http" )
435     HANDLE_GUI_URL( mms, "mms" )
436 }
437 #undef HANDLE_GUI_URL
438
439 /**********************************************************************
440  * Create a sout string from a chain
441  **********************************************************************/
442 static char * ChainToPsz( sout_chain_t *p_chain, vlc_bool_t b_root )
443 {
444     int i, j;
445     char psz_output[MAX_CHAIN];
446     char psz_temp[MAX_CHAIN];
447     if( b_root ) sprintf( psz_output, "#" );
448     else psz_output[0] = '\0';
449
450     for( i = 0 ; i< p_chain->i_modules; i++ )
451     {
452         sout_module_t *p_module = p_chain->pp_modules[i];
453         switch( p_module->i_type )
454         {
455         case SOUT_MOD_DUPLICATE:
456             CHAIN_APPEND( "duplicate{" );
457             for( j = 0 ; j < DUPM->i_children ; j ++ )
458             {
459                 char *psz_child = ChainToPsz( DUPM->pp_children[j], VLC_FALSE);
460                 fprintf(stderr, "child %s\n", psz_child);
461                 CHAIN_APPEND( "dst=%s", psz_child );
462                 free( psz_child );
463                 if( j != DUPM->i_children - 1 ) CHAIN_APPEND( "," );
464             }
465             CHAIN_APPEND( "}" );
466             break;
467         case SOUT_MOD_TRANSCODE:
468             CHAIN_APPEND( "transcode{" );
469             if( TRAM->psz_vcodec )
470             {
471                 CHAIN_APPEND( "vcodec=%s,vb=%i,scale=%f", TRAM->psz_vcodec,
472                                      TRAM->i_vb, TRAM->f_scale );
473                 if( TRAM->psz_acodec || TRAM->psz_scodec || TRAM->b_soverlay )
474                     CHAIN_APPEND( "," );
475             }
476             if( TRAM->psz_acodec )
477             {
478                 CHAIN_APPEND( "acodec=%s,ab=%i,channels=%i", TRAM->psz_acodec,
479                               TRAM->i_ab, TRAM->i_channels );
480                 if( TRAM->psz_scodec || TRAM->b_soverlay )
481                     CHAIN_APPEND( "," );
482             }
483             assert( !(TRAM->psz_scodec && TRAM->b_soverlay) );
484             if( TRAM->psz_scodec )
485                 CHAIN_APPEND( "scodec=%s", TRAM->psz_scodec) ;
486             if( TRAM->b_soverlay )
487                 CHAIN_APPEND( "soverlay" );
488             CHAIN_APPEND( "}" );
489             break;
490
491         case SOUT_MOD_DISPLAY:
492             CHAIN_APPEND( "display" )
493             break;
494         case SOUT_MOD_STD:
495             CHAIN_APPEND( "std{access=%s,url=%s,mux=%s}", STDM->psz_access,
496                           STDM->psz_url, STDM->psz_mux );
497         }
498         if( i != p_chain->i_modules - 1 ) CHAIN_APPEND( ":" );
499     }
500     return strdup( psz_output );
501 }
502
503 char * streaming_ChainToPsz( sout_chain_t *p_chain )
504 {
505     return ChainToPsz( p_chain, VLC_TRUE );
506 }
507
508 /**********************************************************************
509  * Handle streaming profiles
510  **********************************************************************/
511
512 #if 0
513 /**
514  * List the available profiles. Fills the pp_profiles list with preinitialized
515  * values.
516  * \param p_this vlc object
517  * \param pi_profiles number of listed profiles
518  * \param pp_profiles array of profiles
519  */
520 void streaming_ProfilesList( vlc_object_t *p_this, int *pi_profiles,
521                              streaming_profile_t **pp_profiles )
522 {
523 }
524
525
526 /** Parse a profile */
527 int streaming_ProfileParse( vlc_object_t *p_this,streaming_profile_t *p_profile,
528                             const char *psz_profile )
529 {
530     module_t *p_module;
531     DECMALLOC_ERR( p_parser, profile_parser_t );
532     assert( p_profile ); assert( psz_profile );
533
534     p_parser->psz_profile = strdup( psz_profile );
535     p_parser->p_profile = p_profile;
536
537     p_this->p_private = (void *)p_parser;
538
539     /* And call the module ! All work is done now */
540     p_module = module_Need( p_this, "profile parser", "", VLC_TRUE );
541     if( !p_module )
542     {
543         msg_Warn( p_this, "parsing profile failed" );
544         return VLC_EGENERIC;
545     }
546     return VLC_SUCCESS;
547 }
548 #endif