From c8f73cd605ac12d98f39edb6b0fe65ddc43dc5dc Mon Sep 17 00:00:00 2001 From: =?utf8?q?Cl=C3=A9ment=20Stenac?= Date: Tue, 24 Jan 2006 22:42:16 +0000 Subject: [PATCH] Add a timing facility (Refs:#473) It is only available in debug mode. To use: stats_TimerStart( vlc_object_t* , char *name ); stats_TimerStop( vlc_object_t *, char *name ); stats_TimerDump( vlc_object_t* , char *name ) will print a line TIMER name : last_time - total_time / number of samples (average time) stats_TimersDumpAll is not implemented yet --- include/vlc_common.h | 1 + include/vlc_messages.h | 65 ++++++++++++++++---- include/vlc_symbols.h | 38 +++++++++--- src/misc/stats.c | 130 ++++++++++++++++++++++++++++++++++++++-- src/playlist/playlist.c | 2 +- 5 files changed, 210 insertions(+), 26 deletions(-) diff --git a/include/vlc_common.h b/include/vlc_common.h index 7f7dfa9da8..c6998632c2 100644 --- a/include/vlc_common.h +++ b/include/vlc_common.h @@ -420,6 +420,7 @@ typedef struct counter_t counter_t; typedef struct counter_sample_t counter_sample_t; typedef struct stats_handler_t stats_handler_t; typedef struct input_stats_t input_stats_t; +typedef struct global_stats_t global_stats_t; /***************************************************************************** diff --git a/include/vlc_messages.h b/include/vlc_messages.h index f51fa52978..4eba79c8b9 100644 --- a/include/vlc_messages.h +++ b/include/vlc_messages.h @@ -207,13 +207,17 @@ VLC_EXPORT( void, __msg_Unsubscribe, ( vlc_object_t *, msg_subscription_t * ) ); * @{ */ +/**************************** + * Generic stats stuff + ****************************/ enum { STATS_LAST, STATS_COUNTER, STATS_MAX, STATS_MIN, - STATS_DERIVATIVE + STATS_DERIVATIVE, + STATS_TIMER }; struct counter_sample_t @@ -246,17 +250,17 @@ struct stats_handler_t VLC_EXPORT( void, stats_HandlerDestroy, (stats_handler_t*) ); #define stats_Update( a,b,c) __stats_Update( VLC_OBJECT( a ), b, c ) -VLC_EXPORT( int, __stats_Update, (vlc_object_t*, char *, vlc_value_t) ); +VLC_EXPORT( int, __stats_Update, (vlc_object_t*, const char *, vlc_value_t) ); #define stats_Create( a,b,c,d ) __stats_Create( VLC_OBJECT(a), b, c, d ) -VLC_EXPORT( int, __stats_Create, (vlc_object_t*, char *, int, int) ); +VLC_EXPORT( int, __stats_Create, (vlc_object_t*, const char *, int, int) ); #define stats_Get( a,b,c,d ) __stats_Create( VLC_OBJECT(a), b, c, d ) -VLC_EXPORT( int, __stats_Get, (vlc_object_t*, int, char *, vlc_value_t*) ); +VLC_EXPORT( int, __stats_Get, (vlc_object_t*, int, const char *, vlc_value_t*) ); #define stats_CounterGet( a,b,c) __stats_CounterGet( VLC_OBJECT(a), b, c ) -VLC_EXPORT( counter_t*, __stats_CounterGet, (vlc_object_t*, int, char * ) ); +VLC_EXPORT( counter_t*, __stats_CounterGet, (vlc_object_t*, int, const char * ) ); #define stats_GetInteger( a,b,c,d ) __stats_GetInteger( VLC_OBJECT(a), b, c, d ) static inline int __stats_GetInteger( vlc_object_t *p_obj, int i_id, - char *psz_name, int *value ) + const char *psz_name, int *value ) { vlc_value_t val; int i_ret = __stats_Get( p_obj, i_id, psz_name, &val ); @@ -266,7 +270,7 @@ static inline int __stats_GetInteger( vlc_object_t *p_obj, int i_id, #define stats_GetFloat(a,b,c,d ) __stats_GetFloat( VLC_OBJECT(a), b, c, d ) static inline int __stats_GetFloat( vlc_object_t *p_obj, int i_id, - char *psz_name, float *value ) + const char *psz_name, float *value ) { vlc_value_t val; int i_ret = __stats_Get( p_obj, i_id, psz_name, &val ); @@ -274,22 +278,25 @@ static inline int __stats_GetFloat( vlc_object_t *p_obj, int i_id, return i_ret; } #define stats_UpdateInteger( a,b,c ) __stats_UpdateInteger( VLC_OBJECT(a),b,c ) -static inline int __stats_UpdateInteger( vlc_object_t *p_obj, char *psz_name, - int i ) +static inline int __stats_UpdateInteger( vlc_object_t *p_obj, + const char *psz_name, int i ) { vlc_value_t val; val.i_int = i; return __stats_Update( p_obj, psz_name, val ); } #define stats_UpdateFloat( a,b,c ) __stats_UpdateFloat( VLC_OBJECT(a),b,c ) -static inline int __stats_UpdateFloat( vlc_object_t *p_obj, char *psz_name, - float f ) +static inline int __stats_UpdateFloat( vlc_object_t *p_obj, + const char *psz_name, float f ) { vlc_value_t val; val.f_float = f; return __stats_Update( p_obj, psz_name, val ); } +/****************** + * Input stats + ******************/ struct input_stats_t { @@ -328,3 +335,39 @@ struct input_stats_t VLC_EXPORT( void, stats_ComputeInputStats, (input_thread_t*, input_stats_t*) ); VLC_EXPORT( void, stats_ReinitInputStats, (input_stats_t *) ); VLC_EXPORT( void, stats_DumpInputStats, (input_stats_t *) ); + +/******************** + * Global stats + *******************/ +struct global_stats_t +{ + vlc_mutex_t lock; + + float f_input_bitrate; + float f_output_bitrate; + + int i_http_clients; +}; + +#define stats_ComputeGlobalStats( a,b) __stats_ComputeGlobalStats( VLC_OBJECT(a),b) +VLC_EXPORT( void, __stats_ComputeGlobalStats, (vlc_object_t*,global_stats_t*)); + + +/********* + * Timing + ********/ +#ifdef DEBUG +#define stats_TimerStart(a,b) __stats_TimerStart( VLC_OBJECT(a), b ) +#define stats_TimerStop(a,b) __stats_TimerStop( VLC_OBJECT(a), b ) +#define stats_TimerDump(a,b) __stats_TimerDump( VLC_OBJECT(a), b ) +#define stats_TimersDumpAll(a) __stats_TimersDumpAll( VLC_OBJECT(a) ) +#else +#define stats_TimerStart(a,b) {} +#define stats_TimerStop(a,b) {} +#define stats_TimerDump(a,b) {} +#define stats_TimersDumpAll(a) {} +#endif +VLC_EXPORT( void,__stats_TimerStart, (vlc_object_t*, const char *) ); +VLC_EXPORT( void,__stats_TimerStop, (vlc_object_t*, const char *) ); +VLC_EXPORT( void,__stats_TimerDump, (vlc_object_t*, const char *) ); +VLC_EXPORT( void,__stats_TimersDumpAll, (vlc_object_t*) ); diff --git a/include/vlc_symbols.h b/include/vlc_symbols.h index b86c51c2dd..804f536ff3 100644 --- a/include/vlc_symbols.h +++ b/include/vlc_symbols.h @@ -65,7 +65,7 @@ int playlist_ItemSetName (playlist_item_t *, char *); void __osd_MenuShow (vlc_object_t *); httpd_url_t * httpd_UrlNewUnique (httpd_host_t *, const char *psz_url, const char *psz_user, const char *psz_password, const vlc_acl_t *p_acl); void httpd_ClientModeStream (httpd_client_t *cl); -int __stats_Create (vlc_object_t*, char *, int, int); +int __stats_Create (vlc_object_t*, const char *, int, int); void httpd_RedirectDelete (httpd_redirect_t *); void __sout_CfgParse (vlc_object_t *, char *psz_prefix, const char **ppsz_options, sout_cfg_t *); vlm_media_t * vlm_MediaNew (vlm_t *, const char *, int); @@ -122,8 +122,8 @@ void vlm_MessageDelete (vlm_message_t *); void vout_SynchroDecode (vout_synchro_t *); int playlist_Delete (playlist_t *, int); void aout_FiltersPlay (aout_instance_t * p_aout, aout_filter_t ** pp_filters, int i_nb_filters, aout_buffer_t ** pp_input_buffer); -int __stats_Update (vlc_object_t*, char *, vlc_value_t); -int __stats_Get (vlc_object_t*, int, char *, vlc_value_t*); +int __stats_Update (vlc_object_t*, const char *, vlc_value_t); +int __stats_Get (vlc_object_t*, int, const char *, vlc_value_t*); char* httpd_ClientIP (httpd_client_t *cl, char *psz_ip); int __intf_UserProgress (vlc_object_t*, const char*, const char*, float); void httpd_FileDelete (httpd_file_t *); @@ -136,6 +136,7 @@ stream_t * __stream_UrlNew (vlc_object_t *p_this, const char *psz_url); sout_mux_t * sout_MuxNew (sout_instance_t*, char *, sout_access_out_t *); stream_t * __stream_DemuxNew (vlc_object_t *p_obj, char *psz_demux, es_out_t *out); int vout_ShowTextRelative (vout_thread_t *, int, char *, text_style_t *, int, int, int, mtime_t); +void __stats_TimerDump (vlc_object_t*, const char *); int block_FifoPut (block_fifo_t *, block_t *); int playlist_ItemAddParent (playlist_item_t *, int,playlist_item_t *); int __var_Create (vlc_object_t *, const char *, int); @@ -147,7 +148,7 @@ int vlc_getnameinfo (const struct sockaddr *, int, char *, int, int *, int); int vlm_ExecuteCommand (vlm_t *, const char *, vlm_message_t **); char * config_GetUserDir (void); httpd_stream_t * httpd_StreamNew (httpd_host_t *, const char *psz_url, const char *psz_mime, const char *psz_user, const char *psz_password, const vlc_acl_t *p_acl); -counter_t* __stats_CounterGet (vlc_object_t*, int, char *); +counter_t* __stats_CounterGet (vlc_object_t*, int, const char *); int __config_GetType (vlc_object_t *, const char *); void __vlc_thread_ready (vlc_object_t *); int playlist_Export (playlist_t *, const char *, const char *); @@ -201,6 +202,7 @@ int vlm_Save (vlm_t *, const char *); int ACL_AddNet (vlc_acl_t *p_acl, const char *psz_ip, int i_len, vlc_bool_t b_allow); void AddMD5 (struct md5_s *, const uint8_t *, uint32_t); void config_Duplicate (module_t *, module_config_t *); +void __stats_TimerStart (vlc_object_t*, const char *); block_t * __block_New (vlc_object_t *, int); void xml_Delete (xml_t *); void __msg_Warn (vlc_object_t *, const char *, ... ) ATTRIBUTE_FORMAT( 2, 3); @@ -214,6 +216,7 @@ void __intf_UserProgressUpdate (vlc_object_t*, int, const char*, float); void __msg_Generic (vlc_object_t *, int, int, const char *, const char *, ... ) ATTRIBUTE_FORMAT( 5, 6); int vlc_closedir_wrapper (void *); int playlist_ServicesDiscoveryAdd (playlist_t *, const char *); +void __stats_ComputeGlobalStats (vlc_object_t*,global_stats_t*); char * vlc_strndup (const char *s, size_t n); void vout_PlacePicture (vout_thread_t *, unsigned int, unsigned int, unsigned int *, unsigned int *, unsigned int *, unsigned int *); float __config_GetFloat (vlc_object_t *, const char *); @@ -370,6 +373,7 @@ subpicture_t * spu_SortSubpictures (spu_t *, mtime_t); playlist_item_t * playlist_LockItemGetByInput (playlist_t *,input_item_t *); int __net_vaPrintf (vlc_object_t *p_this, int fd, v_socket_t *, const char *psz_fmt, va_list args); int vlm_MediaControl (vlm_t *, vlm_media_t *, const char *, const char *, const char *); +void __stats_TimersDumpAll (vlc_object_t*); vlc_bool_t vout_SynchroChoose (vout_synchro_t *, int, int, vlc_bool_t); int playlist_RecursiveNodeSort (playlist_t *, playlist_item_t *,int, int); int64_t vlc_atoll (const char *nptr); @@ -410,6 +414,7 @@ void aout_FormatsPrint (aout_instance_t * p_aout, const char * psz_text, const a char * FromUTF32 (const wchar_t *); void __vout_OSDMessage (vlc_object_t *, int, char *, ...); void intf_StopThread (intf_thread_t *); +void __stats_TimerStop (vlc_object_t*, const char *); stream_t * __stream_MemoryNew (vlc_object_t *p_obj, uint8_t *p_buffer, int64_t i_size, vlc_bool_t i_preserve_memory); void mwait (mtime_t date); void __config_ResetAll (vlc_object_t *); @@ -880,18 +885,24 @@ struct module_symbols_t int (*__intf_UserProgress_inner) (vlc_object_t*, const char*, const char*, float); void (*__intf_UserProgressUpdate_inner) (vlc_object_t*, int, const char*, float); void (*__intf_UserHide_inner) (vlc_object_t *, int); - int (*__stats_Create_inner) (vlc_object_t*, char *, int, int); - int (*__stats_Update_inner) (vlc_object_t*, char *, vlc_value_t); - int (*__stats_Get_inner) (vlc_object_t*, int, char *, vlc_value_t*); + int (*__stats_Create_inner) (vlc_object_t*, const char *, int, int); + int (*__stats_Update_inner) (vlc_object_t*, const char *, vlc_value_t); + int (*__stats_Get_inner) (vlc_object_t*, int, const char *, vlc_value_t*); void (*stats_ComputeInputStats_inner) (input_thread_t*, input_stats_t*); void (*stats_DumpInputStats_inner) (input_stats_t *); void (*stats_ReinitInputStats_inner) (input_stats_t *); - counter_t* (*__stats_CounterGet_inner) (vlc_object_t*, int, char *); + counter_t* (*__stats_CounterGet_inner) (vlc_object_t*, int, const char *); void *__stats_CounterGet_deprecated; input_thread_t * (*__input_CreateThread2_inner) (vlc_object_t *, input_item_t *, char *); void (*stats_HandlerDestroy_inner) (stats_handler_t*); vlc_t * (*vlc_current_object_inner) (int); void (*__var_OptionParse_inner) (vlc_object_t *, const char *); + void *__stats_TimerDumpAll_deprecated; + void (*__stats_TimerDump_inner) (vlc_object_t*, const char *); + void (*__stats_TimerStart_inner) (vlc_object_t*, const char *); + void (*__stats_ComputeGlobalStats_inner) (vlc_object_t*,global_stats_t*); + void (*__stats_TimerStop_inner) (vlc_object_t*, const char *); + void (*__stats_TimersDumpAll_inner) (vlc_object_t*); }; # if defined (__PLUGIN__) # define aout_FiltersCreatePipeline (p_symbols)->aout_FiltersCreatePipeline_inner @@ -1323,6 +1334,11 @@ struct module_symbols_t # define stats_HandlerDestroy (p_symbols)->stats_HandlerDestroy_inner # define vlc_current_object (p_symbols)->vlc_current_object_inner # define __var_OptionParse (p_symbols)->__var_OptionParse_inner +# define __stats_TimerDump (p_symbols)->__stats_TimerDump_inner +# define __stats_TimerStart (p_symbols)->__stats_TimerStart_inner +# define __stats_ComputeGlobalStats (p_symbols)->__stats_ComputeGlobalStats_inner +# define __stats_TimerStop (p_symbols)->__stats_TimerStop_inner +# define __stats_TimersDumpAll (p_symbols)->__stats_TimersDumpAll_inner # elif defined (HAVE_DYNAMIC_PLUGINS) && !defined (__BUILTIN__) /****************************************************************** * STORE_SYMBOLS: store VLC APIs into p_symbols for plugin access. @@ -1757,8 +1773,14 @@ struct module_symbols_t ((p_symbols)->stats_HandlerDestroy_inner) = stats_HandlerDestroy; \ ((p_symbols)->vlc_current_object_inner) = vlc_current_object; \ ((p_symbols)->__var_OptionParse_inner) = __var_OptionParse; \ + ((p_symbols)->__stats_TimerDump_inner) = __stats_TimerDump; \ + ((p_symbols)->__stats_TimerStart_inner) = __stats_TimerStart; \ + ((p_symbols)->__stats_ComputeGlobalStats_inner) = __stats_ComputeGlobalStats; \ + ((p_symbols)->__stats_TimerStop_inner) = __stats_TimerStop; \ + ((p_symbols)->__stats_TimersDumpAll_inner) = __stats_TimersDumpAll; \ (p_symbols)->net_ConvertIPv4_deprecated = NULL; \ (p_symbols)->__stats_CounterGet_deprecated = NULL; \ + (p_symbols)->__stats_TimerDumpAll_deprecated = NULL; \ # endif /* __PLUGIN__ */ # endif /* HAVE_SHARED_LIBVLC */ diff --git a/src/misc/stats.c b/src/misc/stats.c index fb98a2c91d..d24ba179c7 100644 --- a/src/misc/stats.c +++ b/src/misc/stats.c @@ -33,7 +33,7 @@ * Local prototypes *****************************************************************************/ static counter_t *GetCounter( stats_handler_t *p_handler, int i_object_id, - char *psz_name ); + const char *psz_name ); static int stats_CounterUpdate( stats_handler_t *p_handler, counter_t *p_counter, vlc_value_t val ); @@ -80,7 +80,7 @@ void stats_HandlerDestroy( stats_handler_t *p_stats ) * STATS_MAX (keep the maximum passed value), STATS_MIN, or STATS_DERIVATIVE * (keep a time derivative of the value) */ -int __stats_Create( vlc_object_t *p_this, char *psz_name, int i_type, +int __stats_Create( vlc_object_t *p_this, const char *psz_name, int i_type, int i_compute_type ) { counter_t *p_counter; @@ -123,7 +123,8 @@ int __stats_Create( vlc_object_t *p_this, char *psz_name, int i_type, * \param val the vlc_value union containing the new value to aggregate. For * more information on how data is aggregated, \see __stats_Create */ -int __stats_Update( vlc_object_t *p_this, char *psz_name, vlc_value_t val ) +int __stats_Update( vlc_object_t *p_this, const char *psz_name, + vlc_value_t val ) { int i_ret; counter_t *p_counter; @@ -162,7 +163,8 @@ int __stats_Update( vlc_object_t *p_this, char *psz_name, vlc_value_t val ) * retrieved value * \return an error code */ -int __stats_Get( vlc_object_t *p_this, int i_object_id, char *psz_name, vlc_value_t *val ) +int __stats_Get( vlc_object_t *p_this, int i_object_id, + const char *psz_name, vlc_value_t *val ) { counter_t *p_counter; @@ -241,7 +243,7 @@ int __stats_Get( vlc_object_t *p_this, int i_object_id, char *psz_name, vlc_valu * \return the counter, or NULL if not found (or handler not created yet) */ counter_t *__stats_CounterGet( vlc_object_t *p_this, int i_object_id, - char *psz_name ) + const char *psz_name ) { counter_t *p_counter; @@ -328,6 +330,7 @@ void stats_ComputeInputStats( input_thread_t *p_input, vlc_mutex_unlock( &p_stats->lock ); } + void stats_ReinitInputStats( input_stats_t *p_stats ) { p_stats->i_read_packets = p_stats->i_read_bytes = @@ -358,6 +361,121 @@ void stats_DumpInputStats( input_stats_t *p_stats ) vlc_mutex_unlock( &p_stats->lock ); } +void __stats_ComputeGlobalStats( vlc_object_t *p_obj, + global_stats_t *p_stats ) +{ + vlc_list_t *p_list; + int i_index; + vlc_mutex_lock( &p_stats->lock ); + + p_list = vlc_list_find( p_obj, VLC_OBJECT_INPUT, FIND_CHILD ); + if( p_list ) + { + for( i_index = 0; i_index < p_list->i_count ; i_index ++ ) + { + float f_in = 0, f_out = 0; + p_obj = (vlc_object_t *)p_list->p_values[i_index].p_object; + stats_GetFloat( p_obj, p_obj->i_object_id, "input_bitrate", + &f_in ); + stats_GetFloat( p_obj, p_obj->i_object_id, "sout_send_bitrate", + &f_out ); + p_stats->f_input_bitrate += f_in; + p_stats->f_output_bitrate += f_out; + } + vlc_list_release( p_list ); + } + + vlc_mutex_unlock( &p_stats->lock ); +} + +void stats_ReinitGlobalStats( global_stats_t *p_stats ) +{ + p_stats->f_input_bitrate = p_stats->f_output_bitrate = 0.0; +} + + +void __stats_TimerStart( vlc_object_t *p_obj, const char *psz_name ) +{ + counter_t *p_counter = stats_CounterGet( p_obj, + p_obj->p_vlc->i_object_id, + psz_name ); + if( !p_counter ) + { + counter_sample_t *p_sample; + stats_Create( p_obj->p_vlc, psz_name, VLC_VAR_TIME, STATS_TIMER ); + p_counter = stats_CounterGet( p_obj, p_obj->p_vlc->i_object_id, + psz_name ); + if( !p_counter ) return; + /* 1st sample : if started: start_date, else last_time, b_started */ + p_sample = (counter_sample_t *)malloc( sizeof( counter_sample_t ) ); + INSERT_ELEM( p_counter->pp_samples, p_counter->i_samples, + p_counter->i_samples, p_sample ); + p_sample->date = 0; p_sample->value.b_bool = 0; + /* 2nd sample : global_time, i_samples */ + p_sample = (counter_sample_t *)malloc( sizeof( counter_sample_t ) ); + INSERT_ELEM( p_counter->pp_samples, p_counter->i_samples, + p_counter->i_samples, p_sample ); + p_sample->date = 0; p_sample->value.i_int = 0; + } + if( p_counter->pp_samples[0]->value.b_bool == VLC_TRUE ) + { + msg_Warn( p_obj, "timer %s was already started !", psz_name ); + return; + } + p_counter->pp_samples[0]->value.b_bool = VLC_TRUE; + p_counter->pp_samples[0]->date = mdate(); +} + +void __stats_TimerStop( vlc_object_t *p_obj, const char *psz_name ) +{ + counter_t *p_counter = stats_CounterGet( p_obj, + p_obj->p_vlc->i_object_id, + psz_name ); + if( !p_counter || p_counter->i_samples != 2 ) + { + msg_Err( p_obj, "timer %s does not exist", psz_name ); + return; + } + p_counter->pp_samples[0]->value.b_bool = VLC_FALSE; + p_counter->pp_samples[1]->value.i_int += 1; + p_counter->pp_samples[0]->date = mdate() - p_counter->pp_samples[0]->date; + p_counter->pp_samples[1]->date += p_counter->pp_samples[0]->date; +} + +void __stats_TimerDump( vlc_object_t *p_obj, const char *psz_name ) +{ + mtime_t last, total; + int i_total; + counter_t *p_counter = stats_CounterGet( p_obj, + p_obj->p_vlc->i_object_id, + psz_name ); + if( !p_counter || p_counter->i_samples != 2 ) + { + msg_Err( p_obj, "timer %s does not exist", psz_name ); + return; + } + i_total = p_counter->pp_samples[1]->value.i_int; + total = p_counter->pp_samples[1]->date; + if( p_counter->pp_samples[0]->value.b_bool == VLC_TRUE ) + { + last = mdate() - p_counter->pp_samples[0]->date; + i_total += 1; + total += last; + } + else + { + last = p_counter->pp_samples[0]->date; + } + msg_Dbg( p_obj, "TIMER %s : %.3f ms - Total %.3f ms / %i intvls (Avg %.3f ms)", + psz_name, (float)last/1000, (float)total/1000, i_total, + (float)(total)/(1000*(float)i_total ) ); +} + +void __stats_TimersDumpAll( vlc_object_t *p_obj ) +{ + +} + /******************************************************************** * Following functions are local @@ -493,7 +611,7 @@ static int stats_CounterUpdate( stats_handler_t *p_handler, } static counter_t *GetCounter( stats_handler_t *p_handler, int i_object_id, - char *psz_name ) + const char *psz_name ) { int i; for( i = 0; i< p_handler->i_counters; i++ ) diff --git a/src/playlist/playlist.c b/src/playlist/playlist.c index c15b3426a1..5fffcbc328 100644 --- a/src/playlist/playlist.c +++ b/src/playlist/playlist.c @@ -907,7 +907,6 @@ static playlist_item_t * NextItem( playlist_t *p_playlist ) /* Calculate time needed */ int64_t start = mdate(); #endif - /* Handle quickly a few special cases */ /* No items to play */ @@ -1181,6 +1180,7 @@ static playlist_item_t * NextItem( playlist_t *p_playlist ) { msg_Info( p_playlist, "nothing to play" ); } + return p_new; } -- 2.39.2