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