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