61
68
/* mrl_Parse: parse psz_mrl and fill p_mrl */
62
static int mrl_Parse( mrl_t *p_mrl, char *psz_mrl );
69
static int mrl_Parse( mrl_t *p_mrl, const char *psz_mrl );
63
70
/* mrl_Clean: clean p_mrl after a call to mrl_Parse */
64
71
static void mrl_Clean( mrl_t *p_mrl );
66
#define FREE( p ) if( p ) { free( p ); (p) = NULL; }
68
73
/*****************************************************************************
69
74
* sout_NewInstance: creates a new stream output instance
70
75
*****************************************************************************/
71
76
sout_instance_t *__sout_NewInstance( vlc_object_t *p_parent, char * psz_dest )
78
static const char typename[] = "stream output";
73
79
sout_instance_t *p_sout;
77
if( var_Get( p_parent, "sout-keep", &keep ) < 0 )
79
msg_Warn( p_parent, "cannot get sout-keep value" );
80
keep.b_bool = VLC_FALSE;
84
if( ( p_sout = vlc_object_find( p_parent, VLC_OBJECT_SOUT,
85
FIND_ANYWHERE ) ) != NULL )
87
if( !strcmp( p_sout->psz_sout, psz_dest ) )
89
msg_Dbg( p_parent, "sout keep: reusing sout" );
90
msg_Dbg( p_parent, "sout keep: you probably want to use "
91
"gather stream_out" );
92
vlc_object_detach( p_sout );
93
vlc_object_attach( p_sout, p_parent );
94
vlc_object_release( p_sout );
99
msg_Dbg( p_parent, "sout keep: destroying unusable sout" );
100
vlc_object_release( p_sout );
101
sout_DeleteInstance( p_sout );
105
else if( !keep.b_bool )
107
while( ( p_sout = vlc_object_find( p_parent, VLC_OBJECT_SOUT,
108
FIND_PARENT ) ) != NULL )
110
msg_Dbg( p_parent, "sout keep: destroying old sout" );
111
vlc_object_release( p_sout );
112
sout_DeleteInstance( p_sout );
116
81
/* *** Allocate descriptor *** */
117
p_sout = vlc_object_create( p_parent, VLC_OBJECT_SOUT );
82
p_sout = vlc_custom_create( p_parent, sizeof( *p_sout ),
83
VLC_OBJECT_GENERIC, typename );
118
84
if( p_sout == NULL )
120
msg_Err( p_parent, "out of memory" );
124
87
/* *** init descriptor *** */
125
88
p_sout->psz_sout = strdup( psz_dest );
142
105
/* attach it for inherit */
143
106
vlc_object_attach( p_sout, p_parent );
145
/* Create statistics */
146
stats_Create( p_parent, "sout_sent_packets", STATS_SOUT_SENT_PACKETS,
147
VLC_VAR_INTEGER, STATS_COUNTER );
148
stats_Create( p_parent, "sout_sent_bytes", STATS_SOUT_SENT_BYTES,
149
VLC_VAR_INTEGER, STATS_COUNTER );
150
stats_Create( p_parent, "sout_send_bitrate", STATS_SOUT_SEND_BITRATE,
151
VLC_VAR_FLOAT, STATS_DERIVATIVE );
152
p_counter = stats_CounterGet( p_parent, p_parent->i_object_id,
153
STATS_SOUT_SEND_BITRATE );
154
if( p_counter) p_counter->update_interval = 1000000;
109
var_Create( p_sout, "sout-mux-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
156
112
p_sout->p_stream = sout_StreamNew( p_sout, p_sout->psz_chain );
158
113
if( p_sout->p_stream == NULL )
160
115
msg_Err( p_sout, "stream chain failed for `%s'", p_sout->psz_chain );
162
FREE( p_sout->psz_sout );
163
FREE( p_sout->psz_chain );
117
FREENULL( p_sout->psz_sout );
118
FREENULL( p_sout->psz_chain );
165
120
vlc_object_detach( p_sout );
166
vlc_object_destroy( p_sout );
121
vlc_object_release( p_sout );
194
146
vlc_mutex_destroy( &p_sout->lock );
196
148
/* *** free structure *** */
197
vlc_object_destroy( p_sout );
149
vlc_object_release( p_sout );
152
/*****************************************************************************
154
*****************************************************************************/
155
void sout_UpdateStatistic( sout_instance_t *p_sout, sout_statistic_t i_type, int i_delta )
157
input_thread_t *p_input;
158
int i_bytes; /* That's pretty stupid to define it as an integer, it will overflow
161
if( !libvlc_stats (p_sout) )
165
* TODO add a private (ie not VLC_EXPORTed) input_UpdateStatistic for that */
166
p_input = vlc_object_find( p_sout, VLC_OBJECT_INPUT, FIND_PARENT );
167
if( !p_input || p_input->i_state == INIT_S || p_input->i_state == ERROR_S )
172
#define I(c) stats_UpdateInteger( p_input, p_input->p->counters.c, i_delta, NULL )
173
case SOUT_STATISTIC_DECODED_VIDEO:
176
case SOUT_STATISTIC_DECODED_AUDIO:
179
case SOUT_STATISTIC_DECODED_SUBTITLE:
183
case SOUT_STATISTIC_ENCODED_VIDEO:
184
case SOUT_STATISTIC_ENCODED_AUDIO:
185
case SOUT_STATISTIC_ENCODED_SUBTITLE:
186
msg_Warn( p_sout, "Not yet supported statistic type %d", i_type );
190
case SOUT_STATISTIC_SENT_PACKET:
191
I(p_sout_sent_packets);
194
case SOUT_STATISTIC_SENT_BYTE:
195
if( !stats_UpdateInteger( p_input, p_input->p->counters.p_sout_sent_bytes, i_delta, &i_bytes ) )
196
stats_UpdateFloat( p_input, p_input->p->counters.p_sout_send_bitrate, i_bytes, NULL );
200
msg_Err( p_sout, "Invalid statistic type %d (internal error)", i_type );
203
vlc_object_release( p_input );
200
205
/*****************************************************************************
201
206
* Packetizer/Input
202
207
*****************************************************************************/
287
292
* sout_AccessOutNew: allocate a new access out
288
293
*****************************************************************************/
289
294
sout_access_out_t *sout_AccessOutNew( sout_instance_t *p_sout,
290
char *psz_access, char *psz_name )
295
const char *psz_access, const char *psz_name )
297
static const char typename[] = "access out";
292
298
sout_access_out_t *p_access;
295
if( !( p_access = vlc_object_create( p_sout,
296
sizeof( sout_access_out_t ) ) ) )
298
msg_Err( p_sout, "out of memory" );
301
p_access = vlc_custom_create( p_sout, sizeof( *p_access ),
302
VLC_OBJECT_GENERIC, typename );
302
psz_next = sout_CfgCreate( &p_access->psz_access, &p_access->p_cfg,
308
p_access->psz_name = strdup( psz_name ? psz_name : "" );
306
psz_next = config_ChainCreate( &p_access->psz_access, &p_access->p_cfg,
309
p_access->psz_path = strdup( psz_name ? psz_name : "" );
309
310
p_access->p_sout = p_sout;
310
p_access->p_sys = NULL;
311
p_access->p_sys = NULL;
311
312
p_access->pf_seek = NULL;
312
313
p_access->pf_read = NULL;
313
314
p_access->pf_write = NULL;
315
p_access->pf_control = NULL;
314
316
p_access->p_module = NULL;
316
318
p_access->i_writes = 0;
371
373
/*****************************************************************************
372
374
* sout_AccessWrite:
373
375
*****************************************************************************/
374
int sout_AccessOutWrite( sout_access_out_t *p_access, block_t *p_buffer )
376
ssize_t sout_AccessOutWrite( sout_access_out_t *p_access, block_t *p_buffer )
378
const unsigned i_packets_gather = 30;
377
379
p_access->i_writes++;
378
380
p_access->i_sent_bytes += p_buffer->i_buffer;
379
if( p_access->p_libvlc->b_stats && p_access->i_writes % 30 == 0 )
381
if( (p_access->i_writes % i_packets_gather) == 0 )
381
/* Access_out -> sout_instance -> input_thread_t */
382
input_thread_t *p_input =
383
(input_thread_t *)vlc_object_find( p_access, VLC_OBJECT_INPUT,
387
stats_UpdateInteger( p_input, STATS_SOUT_SENT_PACKETS, 30, NULL );
388
stats_UpdateInteger( p_input, STATS_SOUT_SENT_BYTES,
389
p_access->i_sent_bytes, &i_total );
390
stats_UpdateFloat( p_input, STATS_SOUT_SEND_BITRATE, (float)i_total,
392
p_access->i_sent_bytes = 0;
393
vlc_object_release( p_input );
383
sout_UpdateStatistic( p_access->p_sout, SOUT_STATISTIC_SENT_PACKET, i_packets_gather );
384
sout_UpdateStatistic( p_access->p_sout, SOUT_STATISTIC_SENT_BYTE, p_access->i_sent_bytes );
385
p_access->i_sent_bytes = 0;
396
387
return p_access->pf_write( p_access, p_buffer );
391
* sout_AccessOutControl
393
int sout_AccessOutControl (sout_access_out_t *access, int query, va_list args)
395
return (access->pf_control) ? access->pf_control (access, query, args)
399
399
/*****************************************************************************
400
400
* sout_MuxNew: create a new mux
401
401
*****************************************************************************/
402
402
sout_mux_t * sout_MuxNew( sout_instance_t *p_sout, char *psz_mux,
403
403
sout_access_out_t *p_access )
405
static const char typename[] = "mux";
405
406
sout_mux_t *p_mux;
408
p_mux = vlc_object_create( p_sout, sizeof( sout_mux_t ) );
409
p_mux = vlc_custom_create( p_sout, sizeof( *p_mux ), VLC_OBJECT_GENERIC,
409
411
if( p_mux == NULL )
411
msg_Err( p_sout, "out of memory" );
415
414
p_mux->p_sout = p_sout;
416
psz_next = sout_CfgCreate( &p_mux->psz_mux, &p_mux->p_cfg, psz_mux );
417
if( psz_next ) free( psz_next );
415
psz_next = config_ChainCreate( &p_mux->psz_mux, &p_mux->p_cfg, psz_mux );
419
418
p_mux->p_access = p_access;
420
419
p_mux->pf_control = NULL;
427
426
p_mux->p_sys = NULL;
428
427
p_mux->p_module = NULL;
430
p_mux->b_add_stream_any_time = VLC_FALSE;
431
p_mux->b_waiting_stream = VLC_TRUE;
429
p_mux->b_add_stream_any_time = false;
430
p_mux->b_waiting_stream = true;
432
431
p_mux->i_add_stream_start = -1;
434
433
vlc_object_attach( p_mux, p_sout );
436
435
p_mux->p_module =
437
module_Need( p_mux, "sout mux", p_mux->psz_mux, VLC_TRUE );
436
module_Need( p_mux, "sout mux", p_mux->psz_mux, true );
439
438
if( p_mux->p_module == NULL )
441
FREE( p_mux->psz_mux );
440
FREENULL( p_mux->psz_mux );
443
442
vlc_object_detach( p_mux );
444
vlc_object_destroy( p_mux );
443
vlc_object_release( p_mux );
448
447
/* *** probe mux capacity *** */
449
448
if( p_mux->pf_control )
451
int b_answer = VLC_FALSE;
450
int b_answer = false;
453
452
if( sout_MuxControl( p_mux, MUX_CAN_ADD_STREAM_WHILE_MUXING,
456
b_answer = VLC_FALSE;
461
460
msg_Dbg( p_sout, "muxer support adding stream at any time" );
462
p_mux->b_add_stream_any_time = VLC_TRUE;
463
p_mux->b_waiting_stream = VLC_FALSE;
461
p_mux->b_add_stream_any_time = true;
462
p_mux->b_waiting_stream = false;
465
464
/* If we control the output pace then it's better to wait before
466
465
* starting muxing (generates better streams/files). */
467
466
if( !p_sout->i_out_pace_nocontrol )
471
470
else if( sout_MuxControl( p_mux, MUX_GET_ADD_STREAM_WAIT,
474
b_answer = VLC_FALSE;
479
478
msg_Dbg( p_sout, "muxer prefers to wait for all ES before "
480
479
"starting to mux" );
481
p_mux->b_waiting_stream = VLC_TRUE;
480
p_mux->b_waiting_stream = true;
759
755
* return a pointer on the rest
760
756
* XXX: psz_chain is modified
762
#define SKIPSPACE( p ) { while( *p && ( *p == ' ' || *p == '\t' ) ) p++; }
763
#define SKIPTRAILINGSPACE( p, e ) \
764
{ while( e > p && ( *(e-1) == ' ' || *(e-1) == '\t' ) ) e--; }
766
/* go accross " " and { } */
767
static char *_get_chain_end( char *str )
775
if( !*p || *p == ',' || *p == '}' ) return p;
777
if( *p != '{' && *p != '"' && *p != '\'' )
783
if( *p == '{' ) c = '}';
791
if( *p == c ) return ++p;
792
else if( *p == '{' && c == '}' ) p = _get_chain_end( p );
798
char *sout_CfgCreate( char **ppsz_name, sout_cfg_t **pp_cfg, char *psz_chain )
800
sout_cfg_t *p_cfg = NULL;
806
if( !p ) return NULL;
810
while( *p && *p != '{' && *p != ':' && *p != ' ' && *p != '\t' ) p++;
812
if( p == psz_chain ) return NULL;
814
*ppsz_name = strndup( psz_chain, p - psz_chain );
832
while( *p && *p != '=' && *p != ',' && *p != '{' && *p != '}' &&
833
*p != ' ' && *p != '\t' ) p++;
835
/* fprintf( stderr, "name=%s - rest=%s\n", psz_name, p ); */
838
fprintf( stderr, "invalid options (empty)" );
842
cfg.psz_name = strndup( psz_name, p - psz_name );
846
if( *p == '=' || *p == '{' )
849
vlc_bool_t b_keep_brackets = (*p == '{');
853
end = _get_chain_end( p );
856
cfg.psz_value = NULL;
860
/* Skip heading and trailing spaces.
861
* This ain't necessary but will avoid simple
868
cfg.psz_value = NULL;
872
if( *p == '\'' || *p == '"' ||
873
( !b_keep_brackets && *p == '{' ) )
877
if( *(end-1) != '\'' && *(end-1) == '"' )
878
SKIPTRAILINGSPACE( p, end );
880
if( end - 1 <= p ) cfg.psz_value = NULL;
881
else cfg.psz_value = strndup( p, end -1 - p );
885
SKIPTRAILINGSPACE( p, end );
886
if( end <= p ) cfg.psz_value = NULL;
887
else cfg.psz_value = strndup( p, end - p );
896
cfg.psz_value = NULL;
902
p_cfg->p_next = malloc( sizeof( sout_cfg_t ) );
903
memcpy( p_cfg->p_next, &cfg, sizeof( sout_cfg_t ) );
905
p_cfg = p_cfg->p_next;
909
p_cfg = malloc( sizeof( sout_cfg_t ) );
910
memcpy( p_cfg, &cfg, sizeof( sout_cfg_t ) );
925
if( *p == ':' ) return( strdup( p + 1 ) );
930
static void sout_CfgDestroy( sout_cfg_t *p_cfg )
932
while( p_cfg != NULL )
936
p_next = p_cfg->p_next;
938
FREE( p_cfg->psz_name );
939
FREE( p_cfg->psz_value );
946
void __sout_CfgParse( vlc_object_t *p_this, char *psz_prefix,
947
const char **ppsz_options, sout_cfg_t *cfg )
953
/* First, var_Create all variables */
954
for( i = 0; ppsz_options[i] != NULL; i++ )
956
asprintf( &psz_name, "%s%s", psz_prefix,
957
*ppsz_options[i] == '*' ? &ppsz_options[i][1] : ppsz_options[i] );
959
i_type = config_GetType( p_this, psz_name );
961
var_Create( p_this, psz_name, i_type | VLC_VAR_DOINHERIT );
965
/* Now parse options and set value */
966
if( psz_prefix == NULL ) psz_prefix = "";
971
vlc_bool_t b_yes = VLC_TRUE;
972
vlc_bool_t b_once = VLC_FALSE;
973
module_config_t *p_conf;
975
if( cfg->psz_name == NULL || *cfg->psz_name == '\0' )
980
for( i = 0; ppsz_options[i] != NULL; i++ )
982
if( !strcmp( ppsz_options[i], cfg->psz_name ) )
986
if( ( !strncmp( cfg->psz_name, "no-", 3 ) &&
987
!strcmp( ppsz_options[i], cfg->psz_name + 3 ) ) ||
988
( !strncmp( cfg->psz_name, "no", 2 ) &&
989
!strcmp( ppsz_options[i], cfg->psz_name + 2 ) ) )
995
if( *ppsz_options[i] == '*' &&
996
!strcmp( &ppsz_options[i][1], cfg->psz_name ) )
1003
if( ppsz_options[i] == NULL )
1005
msg_Warn( p_this, "option %s is unknown", cfg->psz_name );
1011
asprintf( &psz_name, "%s%s", psz_prefix, b_once ? &ppsz_options[i][1] : ppsz_options[i] );
1013
/* Check if the option is deprecated */
1014
p_conf = config_FindConfig( p_this, psz_name );
1016
/* This is basically cut and paste from src/misc/configuration.c
1017
* with slight changes */
1018
if( p_conf && p_conf->psz_current )
1020
if( !strcmp( p_conf->psz_current, "SUPPRESSED" ) )
1022
msg_Err( p_this, "Option %s is no longer used.",
1026
else if( p_conf->b_strict )
1028
msg_Err( p_this, "Option %s is deprecated. Use %s instead.",
1029
p_conf->psz_name, p_conf->psz_current );
1030
/* TODO: this should return an error and end option parsing
1031
* ... but doing this would change the VLC API and all the
1032
* modules so i'll do it later */
1037
msg_Warn( p_this, "Option %s is deprecated. You should use "
1038
"%s instead.", p_conf->psz_name, p_conf->psz_current );
1040
psz_name = strdup( p_conf->psz_current );
1043
/* </Check if the option is deprecated> */
1045
/* get the type of the variable */
1046
i_type = config_GetType( p_this, psz_name );
1049
msg_Warn( p_this, "unknown option %s (value=%s)",
1050
cfg->psz_name, cfg->psz_value );
1053
if( i_type != VLC_VAR_BOOL && cfg->psz_value == NULL )
1055
msg_Warn( p_this, "missing value for option %s", cfg->psz_name );
1058
if( i_type != VLC_VAR_STRING && b_once )
1060
msg_Warn( p_this, "*option_name need to be a string option" );
1069
case VLC_VAR_INTEGER:
1070
val.i_int = strtol( cfg->psz_value ? cfg->psz_value : "0",
1074
val.f_float = atof( cfg->psz_value ? cfg->psz_value : "0" );
1076
case VLC_VAR_STRING:
1077
case VLC_VAR_MODULE:
1078
val.psz_string = cfg->psz_value;
1081
msg_Warn( p_this, "unhandled config var type" );
1082
memset( &val, 0, sizeof( vlc_value_t ) );
1089
var_Get( p_this, psz_name, &val2 );
1090
if( *val2.psz_string )
1092
free( val2.psz_string );
1093
msg_Dbg( p_this, "ignoring option %s (not first occurrence)", psz_name );
1096
free( val2.psz_string );
1098
var_Set( p_this, psz_name, val );
1099
msg_Dbg( p_this, "set sout option: %s to %s", psz_name, cfg->psz_value );
1109
760
* XXX name and p_cfg are used (-> do NOT free them)
1111
762
sout_stream_t *sout_StreamNew( sout_instance_t *p_sout, char *psz_chain )
764
static const char typename[] = "stream out";
1113
765
sout_stream_t *p_stream;
1115
767
if( !psz_chain )
1155
804
vlc_object_detach( p_stream );
1156
805
if( p_stream->p_module ) module_Unneed( p_stream, p_stream->p_module );
1158
FREE( p_stream->psz_name );
1159
FREE( p_stream->psz_next );
807
FREENULL( p_stream->psz_name );
808
FREENULL( p_stream->psz_next );
1161
sout_CfgDestroy( p_stream->p_cfg );
810
config_ChainDestroy( p_stream->p_cfg );
1163
812
msg_Dbg( p_stream, "destroying chain done" );
1164
vlc_object_destroy( p_stream );
813
vlc_object_release( p_stream );
1167
static char *_sout_stream_url_to_chain( vlc_object_t *p_this, char *psz_url )
816
static char *_sout_stream_url_to_chain( vlc_object_t *p_this,
817
const char *psz_url )
1170
char *psz_chain, *p;
1172
822
mrl_Parse( &mrl, psz_url );
1173
p = psz_chain = malloc( 500 + strlen( mrl.psz_way ) +
1174
strlen( mrl.psz_access ) +
1175
strlen( mrl.psz_name ) );
1178
if( config_GetInt( p_this, "sout-display" ) )
824
/* Check if the URLs goes to #rtp - otherwise we'll use #standard */
825
static const char rtplist[] = "dccp\0sctp\0tcp\0udplite\0";
826
for (const char *a = rtplist; *a; a += strlen (a) + 1)
827
if (strcmp (a, mrl.psz_access) == 0)
830
if (strcmp (mrl.psz_access, "rtp") == 0)
1180
p += sprintf( p, "duplicate{dst=display,dst=std{mux=\"%s\","
1181
"access=\"%s\",dst=\"%s\"}}",
1182
mrl.psz_way, mrl.psz_access, mrl.psz_name );
833
/* For historical reasons, rtp:// means RTP over UDP */
834
strcpy (mrl.psz_access, "udp");
836
if (mrl.psz_name[0] == '[')
838
port = strstr (mrl.psz_name, "]:");
843
port = strchr (mrl.psz_name, ':');
845
*port++ = '\0'; /* erase ':' */
847
if (asprintf (&psz_chain,
848
"rtp{mux=\"%s\",proto=\"%s\",dst=\"%s%s%s\"}",
849
mrl.psz_way, mrl.psz_access, mrl.psz_name,
850
port ? "\",port=\"" : "", port ? port : "") == -1)
1186
p += sprintf( p, "std{mux=\"%s\",access=\"%s\",dst=\"%s\"}",
1187
mrl.psz_way, mrl.psz_access, mrl.psz_name );
855
/* Convert the URL to a basic standard sout chain */
856
if (asprintf (&psz_chain,
857
"standard{mux=\"%s\",access=\"%s\",dst=\"%s\"}",
858
mrl.psz_way, mrl.psz_access, mrl.psz_name) == -1)
862
/* Duplicate and wrap if sout-display is on */
863
if (psz_chain && (config_GetInt( p_this, "sout-display" ) > 0))
866
if (asprintf (&tmp, "duplicate{dst=display,dst=%s}", tmp) == -1)
1190
872
mrl_Clean( &mrl );
1191
return( psz_chain );