7dc344eae9818b1cd1fb636b488619d3a02e82b6
[vlc.git] / 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 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 sout_std_t *streaming_ChainAddStd( sout_chain_t *p_chain, char *psz_access,
58                                    char *psz_mux, char *psz_url )
59 {
60     DECMALLOC_NULL( p_module, sout_module_t );
61     MALLOC_NULL( STDM, sout_std_t );
62     p_module->i_type = SOUT_MOD_STD;
63     STDM->psz_mux = strdup( psz_mux );
64     STDM->psz_access = strdup( psz_access );
65     STDM->psz_url = strdup( psz_url );
66     TAB_APPEND( p_chain->i_modules, p_chain->pp_modules, p_module );
67     return STDM;
68 }
69 /** Add a new display element to a streaming chain
70  * \return the new element
71  */
72 sout_display_t *streaming_ChainAddDisplay( sout_chain_t *p_chain )
73 {
74     DECMALLOC_NULL( p_module, sout_module_t );
75     MALLOC_NULL( DISM, sout_display_t );
76     p_module->i_type = SOUT_MOD_DISPLAY;
77     TAB_APPEND( p_chain->i_modules, p_chain->pp_modules, p_module );
78     return DISM;
79 }
80
81 /** Add a new transcode element to a streaming chain
82  * \return the new element
83  */
84 sout_transcode_t *streaming_ChainAddTranscode( sout_chain_t *p_chain,
85                         char *psz_vcodec, char * psz_acodec, char * psz_scodec,
86                         int i_vb, float f_scale, int i_ab, int i_channels,
87                         vlc_bool_t b_soverlay, char *psz_additional )
88 {
89     DECMALLOC_NULL( p_module, sout_module_t );
90     MALLOC_NULL( TRAM, sout_transcode_t );
91     p_module->i_type = SOUT_MOD_TRANSCODE;
92     memset( TRAM, 0, sizeof( sout_transcode_t ) );
93     assert( !( b_soverlay && psz_scodec ) );
94     if( psz_vcodec ) TRAM->psz_vcodec = strdup( psz_vcodec );
95     if( psz_acodec ) TRAM->psz_acodec = strdup( psz_acodec );
96     if( psz_scodec ) TRAM->psz_scodec = strdup( psz_scodec );
97     TRAM->i_vb = i_vb; TRAM->i_ab = i_ab; TRAM->f_scale = f_scale;
98     TRAM->i_channels = i_channels; TRAM->b_soverlay = b_soverlay;
99     if( psz_additional ) TRAM->psz_additional = strdup( psz_additional );
100     TAB_APPEND( p_chain->i_modules, p_chain->pp_modules, p_module );
101     return TRAM;
102 }
103
104 /** Add a new clean child chain to an existing duplicate element */
105 void streaming_DupAddChild( sout_duplicate_t *p_dup )
106 {
107     assert( p_dup );
108     sout_chain_t * p_child = streaming_ChainNew();
109     TAB_APPEND( p_dup->i_children, p_dup->pp_children, p_child );
110 }
111
112 #define DUP_OR_CHAIN p_dup ? p_dup->pp_children[p_dup->i_children-1] : p_chain
113
114 #define ADD_OPT( format, args... ) { \
115     char *psz_opt; asprintf( &psz_opt, format, ##args ); \
116     INSERT_ELEM( p_chain->ppsz_options, p_chain->i_options, p_chain->i_options,\
117                  psz_opt );\
118     free( psz_opt ); }
119
120 /** Clean up a chain (recursively if it has some children) */
121 void streaming_ChainClean( sout_chain_t *p_chain )
122 {
123     int i,j;
124     sout_module_t *p_module;
125     if( p_chain->i_modules )
126     {
127         for( i = p_chain->i_modules -1; i >= 0 ; i-- )
128         {
129             p_module = p_chain->pp_modules[i];
130             switch( p_module->i_type )
131             {
132             case SOUT_MOD_DUPLICATE:
133                 if( DUPM->i_children == 0 ) break;
134                 for( j = DUPM->i_children - 1 ; j >= 0; j-- )
135                     streaming_ChainClean( DUPM->pp_children[j] );
136                 break;
137             case SOUT_MOD_STD:
138                 FREENULL( STDM->psz_url );
139                 FREENULL( STDM->psz_name );
140                 FREENULL( STDM->psz_group );
141                 break;
142             case SOUT_MOD_TRANSCODE:
143                 FREENULL( TRAM->psz_vcodec );
144                 FREENULL( TRAM->psz_acodec );
145                 FREENULL( TRAM->psz_scodec );
146                 FREENULL( TRAM->psz_venc );
147                 FREENULL( TRAM->psz_aenc );
148                 FREENULL( TRAM->psz_additional );
149                 break;
150             }
151             REMOVE_ELEM( p_chain->pp_modules, p_chain->i_modules, i );
152             free( p_module );
153         }
154     }
155 }
156
157 /**********************************************************************
158  * Parameters handling
159  **********************************************************************/
160
161 #define APPLY_PSZ( element, field ) case element: \
162 streaming_ParameterApply( p_param, &p_module->field, NULL, NULL, NULL ); break;
163 #define APPLY_INT( element, field ) case element: \
164 streaming_ParameterApply( p_param, NULL, &p_module->field, NULL, NULL ); break;
165 #define APPLY_FLOAT( element, field ) case element: \
166 streaming_ParameterApply( p_param, NULL, NULL, &p_module->field, NULL ); break;
167 #define APPLY_BOOL( element, field ) case element: \
168 streaming_ParameterApply( p_param, NULL, NULL, NULL, &p_module->field ); break;
169
170 /** Apply the parameters for the Std module. It will copy the values from
171  * the parameters to the fields themselves
172  */
173 void streaming_StdParametersApply( sout_std_t *p_module )
174 {
175     int i;
176     for( i = 0 ; i< p_module->i_params; i++ )
177     {
178         sout_param_t *p_param = p_module->pp_params[i];
179         switch( p_param->i_element )
180         {
181             APPLY_PSZ( PSZ_MUX, psz_mux );
182             APPLY_PSZ( PSZ_ACCESS, psz_access );
183             APPLY_PSZ( PSZ_URL, psz_url );
184             APPLY_PSZ( PSZ_NAME, psz_name );
185             APPLY_PSZ( PSZ_GROUP, psz_group );
186         }
187     }
188 }
189
190 /** Apply the parameters for the Transcode module. It will copy the values from
191  * the parameters to the fields themselves
192  */
193 void streaming_TranscodeParametersApply( sout_transcode_t *p_module )
194 {
195     int i;
196     for( i = 0 ; i< p_module->i_params; i++ )
197     {
198         sout_param_t *p_param = p_module->pp_params[i];
199         switch( p_param->i_element )
200         {
201             APPLY_INT( I_VB, i_vb ); APPLY_INT( I_AB, i_ab );
202             APPLY_INT( I_CHANNELS, i_channels ) ;
203             APPLY_FLOAT( F_SCALE, f_scale );
204             APPLY_BOOL( B_SOVERLAY, b_soverlay );
205             APPLY_PSZ( PSZ_VC, psz_vcodec );
206             APPLY_PSZ( PSZ_AC, psz_acodec );
207             APPLY_PSZ( PSZ_SC, psz_scodec );
208             APPLY_PSZ( PSZ_VE, psz_venc ); APPLY_PSZ( PSZ_AE, psz_aenc );
209         }
210     }
211 }
212
213 /** Apply a single parameter
214  * \param p_param the parameter to apply
215  * \param ppsz_dest target string, if param is a string
216  * \param pi_dest target int, if param is an integer
217  * \param pf_dest target float, if param is a float
218  * \param pb_dest target bool, if param is a bool
219  */
220 void streaming_ParameterApply( sout_param_t *p_param, char **ppsz_dest,
221                              int *pi_dest, float *pf_dest, vlc_bool_t *pb_dest )
222 {
223     /* Todo : Handle psz_string like formatting */
224     if( p_param->psz_string )
225     {
226         assert( ppsz_dest );
227         fprintf( stderr, "Unsupported !\n" );
228     }
229     else
230     {
231         switch( p_param->i_type )
232         {
233         case VLC_VAR_INTEGER:
234             assert( pi_dest );
235             *pi_dest = p_param->value.i_int;
236             break;
237         case VLC_VAR_FLOAT:
238             assert( pf_dest );
239             *pf_dest = p_param->value.f_float;
240             break;
241         case VLC_VAR_STRING:
242             assert( ppsz_dest );
243             free( *ppsz_dest );
244             *ppsz_dest = p_param->value.psz_string ?
245                                 strdup( p_param->value.psz_string ) :
246                                 NULL;
247             break;
248         case VLC_VAR_BOOL:
249             assert( pb_dest );
250             *pb_dest = p_param->value.b_bool;
251             break;
252         }
253     }
254 }
255
256 /**********************************************************************
257  * Interaction with streaming GUI descriptors
258  **********************************************************************/
259 #define DO_ENABLE_ACCESS \
260     if( !strcmp( STDM->psz_access, "file" ) )\
261     { \
262         pd->b_file = VLC_TRUE; pd->psz_file = strdup( STDM->psz_url ); \
263     } \
264     else if(  !strcmp( STDM->psz_access, "http" ) )\
265     { \
266         pd->b_http = VLC_TRUE; pd->psz_http = strdup( STDM->psz_url ); \
267     } \
268     else if(  !strcmp( STDM->psz_access, "mms" ) )\
269     { \
270         pd->b_mms = VLC_TRUE; pd->psz_mms = strdup( STDM->psz_url ); \
271     } \
272     else if(  !strcmp( STDM->psz_access, "udp" ) )\
273     { \
274         pd->b_udp = VLC_TRUE; pd->psz_udp = strdup( STDM->psz_url ); \
275     } \
276     else \
277     { \
278         msg_Err( p_this, "unahandled access %s", STDM->psz_access ); \
279     }
280
281
282 /**
283  * Try to convert a chain to a gui descriptor. This is only possible for
284  * "simple" chains.
285  * \param p_this vlc object
286  * \param p_chain the source streaming chain
287  * \param pd the destination gui descriptor object
288  * \return TRUE if the conversion succeeded, false else
289  */
290 vlc_bool_t streaming_ChainToGuiDesc( vlc_object_t *p_this,
291                                   sout_chain_t *p_chain, sout_gui_descr_t *pd )
292 {
293     int j, i_last = 0;
294     sout_module_t *p_module;
295     if( p_chain->i_modules == 0 || p_chain->i_modules > 2 ) return VLC_FALSE;
296
297     if( p_chain->pp_modules[0]->i_type == SOUT_MOD_TRANSCODE )
298     {
299         if( p_chain->i_modules == 1 ) return VLC_FALSE;
300         p_module = p_chain->pp_modules[0];
301         i_last++;
302
303         pd->b_soverlay = TRAM->b_soverlay;
304         pd->i_vb = TRAM->i_vb; pd->i_ab = TRAM->i_ab;
305         pd->i_channels = TRAM->i_channels; pd->f_scale = TRAM->f_scale;
306         if( TRAM->psz_vcodec ) pd->psz_vcodec = strdup( TRAM->psz_vcodec );
307         if( TRAM->psz_acodec ) pd->psz_acodec = strdup( TRAM->psz_acodec );
308         if( TRAM->psz_scodec ) pd->psz_scodec = strdup( TRAM->psz_scodec );
309     }
310     if( p_chain->pp_modules[i_last]->i_type == SOUT_MOD_DUPLICATE )
311     {
312         p_module = p_chain->pp_modules[i_last];
313
314         // Nothing allowed after duplicate. Duplicate mustn't be empty
315         if( p_chain->i_modules > i_last +1 || !DUPM->i_children )
316             return VLC_FALSE;
317         for( j = 0 ; j<  DUPM->i_children ; j++ )
318         {
319             sout_chain_t *p_child = DUPM->pp_children[j];
320             if( p_child->i_modules != 1 ) return VLC_FALSE;
321             p_module = p_child->pp_modules[0];
322             if( p_module->i_type == SOUT_MOD_STD )
323             {
324                 DO_ENABLE_ACCESS
325             }
326             else if( p_module->i_type == SOUT_MOD_DISPLAY )
327                 pd->b_local = VLC_TRUE;
328             else if( p_module->i_type == SOUT_MOD_RTP )
329             {
330                 msg_Err( p_this, "RTP unhandled" );
331                 return VLC_FALSE;
332             }
333         }
334         i_last++;
335     }
336     if( p_chain->pp_modules[i_last]->i_type == SOUT_MOD_STD )
337     {
338         p_module = p_chain->pp_modules[i_last];
339         DO_ENABLE_ACCESS;
340     }
341     else if( p_chain->pp_modules[i_last]->i_type == SOUT_MOD_DISPLAY )
342     {
343         pd->b_local = VLC_TRUE;
344     }
345     else if( p_chain->pp_modules[i_last]->i_type == SOUT_MOD_RTP )
346     {
347         msg_Err( p_this, "RTP unhandled" );
348         return VLC_FALSE;
349
350     }
351     return VLC_TRUE;
352
353 }
354
355 #define HANDLE_GUI_URL( type, access ) if( pd->b_##type ) { \
356         if( p_dup ) streaming_DupAddChild( p_dup ); \
357         if( pd->i_##type > 0 ) \
358         { \
359             char *psz_url; \
360             asprintf( &psz_url, "%s:%i", pd->psz_##type, pd->i_##type ); \
361             streaming_ChainAddStd( DUP_OR_CHAIN, access, pd->psz_mux,\
362                                    psz_url ); \
363             free( psz_url ); \
364         } \
365         else \
366         { \
367             streaming_ChainAddStd( DUP_OR_CHAIN, access, pd->psz_mux,\
368                                    pd->psz_##type );\
369         }\
370     }
371
372 void streaming_GuiDescToChain( vlc_object_t *p_obj, sout_chain_t *p_chain,
373                                sout_gui_descr_t *pd )
374 {
375     sout_duplicate_t *p_dup = NULL;
376     /* Clean up the chain */
377     streaming_ChainClean( p_chain );
378
379     /* Transcode */
380     if( pd->psz_vcodec || pd->psz_acodec || pd->psz_scodec || pd->b_soverlay )
381     {
382         fprintf( stderr, "382 vcodec %s\n", pd->psz_vcodec );
383         streaming_ChainAddTranscode( p_chain, pd->psz_vcodec, pd->psz_acodec,
384                                      pd->psz_scodec, pd->i_vb, pd->f_scale,
385                                      pd->i_ab, pd->i_channels,
386                                      pd->b_soverlay, NULL );
387     }
388     /* #std{} */
389     if( pd->b_local + pd->b_file + pd->b_http + pd->b_mms + pd->b_rtp +
390         pd->b_udp > 1 )
391     {
392         p_dup = streaming_ChainAddDup( p_chain );
393     }
394     if( pd->b_local )
395     {
396         if( p_dup ) streaming_DupAddChild( p_dup );
397         streaming_ChainAddDisplay( DUP_OR_CHAIN );
398     }
399     if( pd->b_file )
400     {
401         if( p_dup ) streaming_DupAddChild( p_dup );
402         streaming_ChainAddStd( DUP_OR_CHAIN, "file", pd->psz_mux,
403                                pd->psz_file );
404     }
405     if( pd->b_udp )
406     {
407         sout_std_t *p_std;
408         if( p_dup ) streaming_DupAddChild( p_dup );
409         if( pd->i_udp > 0 )
410         {
411             char *psz_url;
412             asprintf( &psz_url, "%s:%i", pd->psz_udp, pd->i_udp );
413             p_std = streaming_ChainAddStd( DUP_OR_CHAIN, "udp",
414                                            pd->psz_mux, psz_url );
415             free( psz_url );
416         }
417         else
418         {
419             p_std = streaming_ChainAddStd( DUP_OR_CHAIN, "udp",
420                                            pd->psz_mux, pd->psz_udp );
421         }
422         if( pd->i_ttl ) ADD_OPT( "ttl=%i", pd->i_ttl );
423         if( pd->b_sap )
424         {
425             pd->b_sap = VLC_TRUE;
426             p_std->psz_name = strdup( pd->psz_name );
427             p_std->psz_group = pd->psz_group ? strdup( pd->psz_group ) : NULL;
428         }
429     }
430     HANDLE_GUI_URL( http, "http" )
431     HANDLE_GUI_URL( mms, "mms" )
432 }
433 #undef HANDLE_GUI_URL
434
435 /**********************************************************************
436  * Create a sout string from a chain
437  **********************************************************************/
438 static char * ChainToPsz( sout_chain_t *p_chain, vlc_bool_t b_root )
439 {
440     int i, j;
441     char psz_output[MAX_CHAIN];
442     char psz_temp[MAX_CHAIN];
443     if( b_root ) sprintf( psz_output, "#" );
444     else sprintf( psz_output, "" );
445     for( i = 0 ; i< p_chain->i_modules; i++ )
446     {
447         sout_module_t *p_module = p_chain->pp_modules[i];
448         switch( p_module->i_type )
449         {
450         case SOUT_MOD_DUPLICATE:
451             CHAIN_APPEND( "duplicate{" );
452             for( j = 0 ; j < DUPM->i_children ; j ++ )
453             {
454                 char *psz_child = ChainToPsz( DUPM->pp_children[j], VLC_FALSE);
455                 fprintf(stderr, "child %s\n", psz_child);
456                 CHAIN_APPEND( "dst=%s", psz_child );
457                 free( psz_child );
458                 if( j != DUPM->i_children - 1 ) CHAIN_APPEND( "," );
459             }
460             CHAIN_APPEND( "}" );
461             break;
462         case SOUT_MOD_TRANSCODE:
463             CHAIN_APPEND( "transcode{" );
464             if( TRAM->psz_vcodec )
465             {
466                 CHAIN_APPEND( "vcodec=%s,vb=%i,scale=%f", TRAM->psz_vcodec,
467                                      TRAM->i_vb, TRAM->f_scale );
468                 if( TRAM->psz_acodec || TRAM->psz_scodec || TRAM->b_soverlay )
469                     CHAIN_APPEND( "," );
470             }
471             if( TRAM->psz_acodec )
472             {
473                 CHAIN_APPEND( "acodec=%s,ab=%i,channels=%i", TRAM->psz_acodec,
474                               TRAM->i_ab, TRAM->i_channels );
475                 if( TRAM->psz_scodec || TRAM->b_soverlay )
476                     CHAIN_APPEND( "," );
477             }
478             assert( !(TRAM->psz_scodec && TRAM->b_soverlay) );
479             if( TRAM->psz_scodec )
480                 CHAIN_APPEND( "scodec=%s", TRAM->psz_scodec) ;
481             if( TRAM->b_soverlay )
482                 CHAIN_APPEND( "soverlay" );
483             CHAIN_APPEND( "}" );
484             break;
485
486         case SOUT_MOD_DISPLAY:
487             CHAIN_APPEND( "display" )
488             break;
489         case SOUT_MOD_STD:
490             CHAIN_APPEND( "std{access=%s,url=%s,mux=%s}", STDM->psz_access,
491                           STDM->psz_url, STDM->psz_mux );
492         }
493         if( i != p_chain->i_modules - 1 ) CHAIN_APPEND( ":" );
494     }
495     return strdup( psz_output );
496 }
497
498 char * streaming_ChainToPsz( sout_chain_t *p_chain )
499 {
500     return ChainToPsz( p_chain, VLC_TRUE );
501 }
502
503 /**********************************************************************
504  * Handle streaming profiles
505  **********************************************************************/
506
507 /**
508  * List the available profiles. Fills the pp_profiles list with preinitialized
509  * values.
510  * \param p_this vlc object
511  * \param pi_profiles number of listed profiles
512  * \param pp_profiles array of profiles
513  */
514 void streaming_ProfilesList( vlc_object_t *p_this, int *pi_profiles,
515                              streaming_profile_t **pp_profiles )
516 {
517 }
518
519
520 /** Parse a profile */
521 int streaming_ProfileParse( vlc_object_t *p_this,streaming_profile_t *p_profile,
522                             const char *psz_profile )
523 {
524     module_t *p_module;
525     DECMALLOC_ERR( p_parser, profile_parser_t );
526     assert( p_profile ); assert( psz_profile );
527
528     p_parser->psz_profile = strdup( psz_profile );
529     p_parser->p_profile = p_profile;
530
531     p_this->p_private = (void *)p_parser;
532
533     /* And call the module ! All work is done now */
534     p_module = module_Need( p_this, "profile parser", "", VLC_TRUE );
535     if( !p_module )
536     {
537         msg_Warn( p_this, "parsing profile failed" );
538         return VLC_EGENERIC;
539     }
540     return VLC_SUCCESS;
541 }