]> git.sesse.net Git - vlc/blob - src/misc/stats.c
For consistency, remove references to vlc from libvlc
[vlc] / src / misc / stats.c
1 /*****************************************************************************
2  * stats.c: Statistics handling
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 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdio.h>                                               /* required */
28
29 #include <vlc/vlc.h>
30 #include <vlc/input.h>
31
32 /*****************************************************************************
33  * Local prototypes
34  *****************************************************************************/
35 static int CounterUpdate( vlc_object_t *p_this,
36                           counter_t *p_counter,
37                           vlc_value_t val, vlc_value_t * );
38 static void TimerDump( vlc_object_t *p_this, counter_t *p_counter, vlc_bool_t);
39
40 /*****************************************************************************
41  * Exported functions
42  *****************************************************************************/
43
44 /**
45  * Create a statistics counter
46  * \param p_this a VLC object
47  * \param i_type the type of stored data. One of VLC_VAR_STRING,
48  * VLC_VAR_INTEGER, VLC_VAR_FLOAT
49  * \param i_compute_type the aggregation type. One of STATS_LAST (always
50  * keep the last value), STATS_COUNTER (increment by the passed value),
51  * STATS_MAX (keep the maximum passed value), STATS_MIN, or STATS_DERIVATIVE
52  * (keep a time derivative of the value)
53  */
54 counter_t * __stats_CounterCreate( vlc_object_t *p_this,
55                                    int i_type, int i_compute_type )
56 {
57     counter_t *p_counter = (counter_t*) malloc( sizeof( counter_t ) ) ;
58
59     p_counter->i_compute_type = i_compute_type;
60     p_counter->i_type = i_type;
61     p_counter->i_samples = 0;
62     p_counter->pp_samples = NULL;
63     p_counter->psz_name = NULL;
64
65     p_counter->update_interval = 0;
66     p_counter->last_update = 0;
67
68     return p_counter;
69 }
70
71 /** Update a counter element with new values
72  * \param p_this a VLC object
73  * \param p_counter the counter to update
74  * \param val the vlc_value union containing the new value to aggregate. For
75  * more information on how data is aggregated, \see __stats_Create
76  * \param val_new a pointer that will be filled with new data
77  */
78 int __stats_Update( vlc_object_t *p_this, counter_t *p_counter,
79                     vlc_value_t val, vlc_value_t *val_new )
80 {
81     if( !p_this->p_libvlc_global->b_stats || !p_counter ) return VLC_EGENERIC;
82     return CounterUpdate( p_this, p_counter, val, val_new );
83 }
84
85 /** Get the aggregated value for a counter
86  * \param p_this an object
87  * \param p_counter the counter
88  * \param val a pointer to an initialized vlc_value union. It will contain the
89  * retrieved value
90  * \return an error code
91  */
92 int __stats_Get( vlc_object_t *p_this, counter_t *p_counter, vlc_value_t *val )
93 {
94     if( !p_this->p_libvlc_global->b_stats || !p_counter || p_counter->i_samples == 0 )
95     {
96         val->i_int = val->f_float = 0.0;
97         return VLC_EGENERIC;
98     }
99
100     switch( p_counter->i_compute_type )
101     {
102     case STATS_LAST:
103     case STATS_MIN:
104     case STATS_MAX:
105     case STATS_COUNTER:
106         *val = p_counter->pp_samples[0]->value;
107         break;
108     case STATS_DERIVATIVE:
109         /* Not ready yet */
110         if( p_counter->i_samples < 2 )
111         {
112             val->i_int = 0; val->f_float = 0.0;
113             return VLC_EGENERIC;
114         }
115         if( p_counter->i_type == VLC_VAR_INTEGER )
116         {
117             float f = ( p_counter->pp_samples[0]->value.i_int -
118                         p_counter->pp_samples[1]->value.i_int ) /
119                     (float)(  p_counter->pp_samples[0]->date -
120                               p_counter->pp_samples[1]->date );
121             val->i_int = (int)f;
122         }
123         else
124         {
125             float f = (float)( p_counter->pp_samples[0]->value.f_float -
126                                p_counter->pp_samples[1]->value.f_float ) /
127                       (float)( p_counter->pp_samples[0]->date -
128                                p_counter->pp_samples[1]->date );
129             val->f_float = f;
130         }
131         break;
132     }
133     return VLC_SUCCESS;;
134 }
135
136 void stats_ComputeInputStats( input_thread_t *p_input, input_stats_t *p_stats )
137 {
138     if( !p_input->p_libvlc_global->b_stats ) return;
139
140     vlc_mutex_lock( &p_input->counters.counters_lock );
141     vlc_mutex_lock( &p_stats->lock );
142
143     /* Input */
144     stats_GetInteger( p_input, p_input->counters.p_read_packets,
145                       &p_stats->i_read_packets );
146     stats_GetInteger( p_input, p_input->counters.p_read_bytes,
147                       &p_stats->i_read_bytes );
148     stats_GetFloat( p_input, p_input->counters.p_input_bitrate,
149                     &p_stats->f_input_bitrate );
150     stats_GetInteger( p_input, p_input->counters.p_demux_read,
151                       &p_stats->i_demux_read_bytes );
152     stats_GetFloat( p_input, p_input->counters.p_demux_bitrate,
153                     &p_stats->f_demux_bitrate );
154
155     /* Decoders */
156     stats_GetInteger( p_input, p_input->counters.p_decoded_video,
157                       &p_stats->i_decoded_video );
158     stats_GetInteger( p_input, p_input->counters.p_decoded_audio,
159                       &p_stats->i_decoded_audio );
160
161     /* Sout */
162     if( p_input->counters.p_sout_send_bitrate )
163     {
164         stats_GetInteger( p_input, p_input->counters.p_sout_sent_packets,
165                           &p_stats->i_sent_packets );
166         stats_GetInteger( p_input, p_input->counters.p_sout_sent_bytes,
167                           &p_stats->i_sent_bytes );
168         stats_GetFloat  ( p_input, p_input->counters.p_sout_send_bitrate,
169                           &p_stats->f_send_bitrate );
170     }
171
172     /* Aout */
173     stats_GetInteger( p_input, p_input->counters.p_played_abuffers,
174                       &p_stats->i_played_abuffers );
175     stats_GetInteger( p_input, p_input->counters.p_lost_abuffers,
176                       &p_stats->i_lost_abuffers );
177
178     /* Vouts */
179     stats_GetInteger( p_input, p_input->counters.p_displayed_pictures,
180                       &p_stats->i_displayed_pictures );
181     stats_GetInteger( p_input, p_input->counters.p_lost_pictures,
182                       &p_stats->i_lost_pictures );
183
184     vlc_mutex_unlock( &p_stats->lock );
185     vlc_mutex_unlock( &p_input->counters.counters_lock );
186 }
187
188 void stats_ReinitInputStats( input_stats_t *p_stats )
189 {
190     vlc_mutex_lock( &p_stats->lock );
191     p_stats->i_read_packets = p_stats->i_read_bytes =
192     p_stats->f_input_bitrate = p_stats->f_average_input_bitrate =
193     p_stats->i_demux_read_packets = p_stats->i_demux_read_bytes =
194     p_stats->f_demux_bitrate = p_stats->f_average_demux_bitrate =
195     p_stats->i_displayed_pictures = p_stats->i_lost_pictures =
196     p_stats->i_played_abuffers = p_stats->i_lost_abuffers =
197     p_stats->i_decoded_video = p_stats->i_decoded_audio =
198     p_stats->i_sent_bytes = p_stats->i_sent_packets = p_stats->f_send_bitrate
199      = 0;
200     vlc_mutex_unlock( &p_stats->lock );
201 }
202
203 void stats_DumpInputStats( input_stats_t *p_stats  )
204 {
205     vlc_mutex_lock( &p_stats->lock );
206     /* f_bitrate is in bytes / microsecond
207      * *1000 => bytes / millisecond => kbytes / seconds */
208     fprintf( stderr, "Input : %i (%i bytes) - %f kB/s - "
209                      "Demux : %i (%i bytes) - %f kB/s\n"
210                      " - Vout : %i/%i - Aout : %i/%i - Sout : %f\n",
211                     p_stats->i_read_packets, p_stats->i_read_bytes,
212                     p_stats->f_input_bitrate * 1000,
213                     p_stats->i_demux_read_packets, p_stats->i_demux_read_bytes,
214                     p_stats->f_demux_bitrate * 1000,
215                     p_stats->i_displayed_pictures, p_stats->i_lost_pictures,
216                     p_stats->i_played_abuffers, p_stats->i_lost_abuffers,
217                     p_stats->f_send_bitrate );
218     vlc_mutex_unlock( &p_stats->lock );
219 }
220
221 void __stats_ComputeGlobalStats( vlc_object_t *p_obj, global_stats_t *p_stats )
222 {
223     vlc_list_t *p_list;
224     int i_index;
225
226     if( !p_obj->p_libvlc_global->b_stats ) return;
227
228     vlc_mutex_lock( &p_stats->lock );
229
230     p_list = vlc_list_find( p_obj, VLC_OBJECT_INPUT, FIND_ANYWHERE );
231     if( p_list )
232     {
233         float f_total_in = 0, f_total_out = 0,f_total_demux = 0;
234         for( i_index = 0; i_index < p_list->i_count ; i_index ++ )
235         {
236             float f_in = 0, f_out = 0, f_demux = 0;
237             input_thread_t *p_input = (input_thread_t *)
238                              p_list->p_values[i_index].p_object;
239             vlc_mutex_lock( &p_input->counters.counters_lock );
240             stats_GetFloat( p_obj, p_input->counters.p_input_bitrate, &f_in );
241             if( p_input->counters.p_sout_send_bitrate )
242                 stats_GetFloat( p_obj, p_input->counters.p_sout_send_bitrate,
243                                     &f_out );
244             stats_GetFloat( p_obj, p_input->counters.p_demux_bitrate,
245                                 &f_demux );
246             vlc_mutex_unlock( &p_input->counters.counters_lock );
247             f_total_in += f_in; f_total_out += f_out;f_total_demux += f_demux;
248         }
249         p_stats->f_input_bitrate = f_total_in;
250         p_stats->f_output_bitrate = f_total_out;
251         p_stats->f_demux_bitrate = f_total_demux;
252         vlc_list_release( p_list );
253     }
254
255     vlc_mutex_unlock( &p_stats->lock );
256 }
257
258 void stats_ReinitGlobalStats( global_stats_t *p_stats )
259 {
260     vlc_mutex_lock( &p_stats->lock );
261     p_stats->f_input_bitrate = p_stats->f_output_bitrate = 0.0;
262     vlc_mutex_unlock( &p_stats->lock );
263 }
264
265
266 void __stats_TimerStart( vlc_object_t *p_obj, const char *psz_name,
267                          unsigned int i_id )
268 {
269     int i;
270     counter_t *p_counter = NULL;
271     if( !p_obj->p_libvlc_global->b_stats ) return;
272     vlc_mutex_lock( &p_obj->p_libvlc_global->timer_lock );
273
274     for( i = 0 ; i < p_obj->p_libvlc_global->i_timers; i++ )
275     {
276         if( p_obj->p_libvlc_global->pp_timers[i]->i_id == i_id )
277         {
278             p_counter = p_obj->p_libvlc_global->pp_timers[i];
279             break;
280         }
281     }
282     if( !p_counter )
283     {
284         counter_sample_t *p_sample;
285         p_counter = stats_CounterCreate( p_obj->p_libvlc_global, VLC_VAR_TIME,
286                                          STATS_TIMER );
287         if( !p_counter )
288         {
289             vlc_mutex_unlock( &p_obj->p_libvlc_global->timer_lock );
290             return;
291         }
292         p_counter->psz_name = strdup( psz_name );
293         p_counter->i_id = i_id;
294         INSERT_ELEM( p_obj->p_libvlc_global->pp_timers, p_obj->p_libvlc_global->i_timers,
295                      p_obj->p_libvlc_global->i_timers, p_counter );
296
297         /* 1st sample : if started: start_date, else last_time, b_started */
298         p_sample = (counter_sample_t *)malloc( sizeof( counter_sample_t ) );
299         INSERT_ELEM( p_counter->pp_samples, p_counter->i_samples,
300                      p_counter->i_samples, p_sample );
301         p_sample->date = 0; p_sample->value.b_bool = 0;
302         /* 2nd sample : global_time, i_samples */
303         p_sample = (counter_sample_t *)malloc( sizeof( counter_sample_t ) );
304         INSERT_ELEM( p_counter->pp_samples, p_counter->i_samples,
305                      p_counter->i_samples, p_sample );
306         p_sample->date = 0; p_sample->value.i_int = 0;
307     }
308     if( p_counter->pp_samples[0]->value.b_bool == VLC_TRUE )
309     {
310         msg_Warn( p_obj, "timer %s was already started !", psz_name );
311         vlc_mutex_unlock( &p_obj->p_libvlc_global->timer_lock );
312         return;
313     }
314     p_counter->pp_samples[0]->value.b_bool = VLC_TRUE;
315     p_counter->pp_samples[0]->date = mdate();
316     vlc_mutex_unlock( &p_obj->p_libvlc_global->timer_lock );
317 }
318
319 void __stats_TimerStop( vlc_object_t *p_obj, unsigned int i_id )
320 {
321     counter_t *p_counter = NULL;
322     int i;
323     if( !p_obj->p_libvlc_global->b_stats ) return;
324     vlc_mutex_lock( &p_obj->p_libvlc_global->timer_lock );
325     for( i = 0 ; i < p_obj->p_libvlc_global->i_timers; i++ )
326     {
327         if( p_obj->p_libvlc_global->pp_timers[i]->i_id == i_id )
328         {
329             p_counter = p_obj->p_libvlc_global->pp_timers[i];
330             break;
331         }
332     }
333     if( !p_counter || p_counter->i_samples != 2 )
334     {
335         msg_Err( p_obj, "timer does not exist" );
336         vlc_mutex_unlock( &p_obj->p_libvlc_global->timer_lock );
337         return;
338     }
339     p_counter->pp_samples[0]->value.b_bool = VLC_FALSE;
340     p_counter->pp_samples[1]->value.i_int += 1;
341     p_counter->pp_samples[0]->date = mdate() - p_counter->pp_samples[0]->date;
342     p_counter->pp_samples[1]->date += p_counter->pp_samples[0]->date;
343     vlc_mutex_unlock( &p_obj->p_libvlc_global->timer_lock );
344 }
345
346 void __stats_TimerDump( vlc_object_t *p_obj, unsigned int i_id )
347 {
348     counter_t *p_counter = NULL;
349     int i;
350     if( !p_obj->p_libvlc_global->b_stats ) return;
351     vlc_mutex_lock( &p_obj->p_libvlc_global->timer_lock );
352     for( i = 0 ; i < p_obj->p_libvlc_global->i_timers; i++ )
353     {
354         if( p_obj->p_libvlc_global->pp_timers[i]->i_id == i_id )
355         {
356             p_counter = p_obj->p_libvlc_global->pp_timers[i];
357             break;
358         }
359     }
360     TimerDump( p_obj, p_counter, VLC_TRUE );
361     vlc_mutex_unlock( &p_obj->p_libvlc_global->timer_lock );
362 }
363
364 void __stats_TimersDumpAll( vlc_object_t *p_obj )
365 {
366     int i;
367     if( !p_obj->p_libvlc_global->b_stats ) return;
368     vlc_mutex_lock( &p_obj->p_libvlc_global->timer_lock );
369     for ( i = 0 ; i< p_obj->p_libvlc_global->i_timers ; i++ )
370         TimerDump( p_obj, p_obj->p_libvlc_global->pp_timers[i], VLC_FALSE );
371     vlc_mutex_unlock( &p_obj->p_libvlc_global->timer_lock );
372 }
373
374 void __stats_TimersClean( vlc_object_t *p_obj )
375 {
376     int i;
377     vlc_mutex_lock( &p_obj->p_libvlc_global->timer_lock );
378     for ( i = p_obj->p_libvlc_global->i_timers -1 ; i >= 0; i-- )
379     {
380         counter_t *p_counter = p_obj->p_libvlc_global->pp_timers[i];
381         REMOVE_ELEM( p_obj->p_libvlc_global->pp_timers, p_obj->p_libvlc_global->i_timers, i );
382         stats_CounterClean( p_counter );
383     }
384     vlc_mutex_unlock( &p_obj->p_libvlc_global->timer_lock );
385 }
386
387 void stats_CounterClean( counter_t *p_c )
388 {
389     int i;
390     if( p_c )
391     {
392         i = p_c->i_samples - 1 ;
393         while( i >= 0 )
394         {
395             counter_sample_t *p_s = p_c->pp_samples[i];
396             REMOVE_ELEM( p_c->pp_samples, p_c->i_samples, i );
397             free( p_s );
398             i--;
399         }
400         if( p_c->psz_name ) free( p_c->psz_name );
401         free( p_c );
402     }
403 }
404
405
406 /********************************************************************
407  * Following functions are local
408  ********************************************************************/
409
410 /**
411  * Update a statistics counter, according to its type
412  * If needed, perform a bit of computation (derivative, mostly)
413  * This function must be entered with stats handler lock
414  * \param p_counter the counter to update
415  * \param val the "new" value
416  * \return an error code
417  */
418 static int CounterUpdate( vlc_object_t *p_handler,
419                           counter_t *p_counter,
420                           vlc_value_t val, vlc_value_t *new_val )
421 {
422     switch( p_counter->i_compute_type )
423     {
424     case STATS_LAST:
425     case STATS_MIN:
426     case STATS_MAX:
427         if( p_counter->i_samples > 1)
428         {
429             msg_Err( p_handler, "LAST counter has several samples !" );
430             return VLC_EGENERIC;
431         }
432         if( p_counter->i_type != VLC_VAR_FLOAT &&
433             p_counter->i_type != VLC_VAR_INTEGER &&
434             p_counter->i_compute_type != STATS_LAST )
435         {
436             msg_Err( p_handler, "unable to compute MIN or MAX for this type");
437             return VLC_EGENERIC;
438         }
439
440         if( p_counter->i_samples == 0 )
441         {
442             counter_sample_t *p_new = (counter_sample_t*)malloc(
443                                                sizeof( counter_sample_t ) );
444             p_new->value.psz_string = NULL;
445
446             INSERT_ELEM( p_counter->pp_samples, p_counter->i_samples,
447                          p_counter->i_samples, p_new );
448         }
449         if( p_counter->i_samples == 1 )
450         {
451             /* Update if : LAST or (MAX and bigger) or (MIN and bigger) */
452             if( p_counter->i_compute_type == STATS_LAST ||
453                 ( p_counter->i_compute_type == STATS_MAX &&
454                    ( ( p_counter->i_type == VLC_VAR_INTEGER &&
455                        p_counter->pp_samples[0]->value.i_int > val.i_int ) ||
456                      ( p_counter->i_type == VLC_VAR_FLOAT &&
457                        p_counter->pp_samples[0]->value.f_float > val.f_float )
458                    ) ) ||
459                 ( p_counter->i_compute_type == STATS_MIN &&
460                    ( ( p_counter->i_type == VLC_VAR_INTEGER &&
461                        p_counter->pp_samples[0]->value.i_int < val.i_int ) ||
462                      ( p_counter->i_type == VLC_VAR_FLOAT &&
463                        p_counter->pp_samples[0]->value.f_float < val.f_float )
464                    ) ) )
465             {
466                 if( p_counter->i_type == VLC_VAR_STRING &&
467                     p_counter->pp_samples[0]->value.psz_string )
468                 {
469                     free( p_counter->pp_samples[0]->value.psz_string );
470                 }
471                 p_counter->pp_samples[0]->value = val;
472                 *new_val = p_counter->pp_samples[0]->value;
473             }
474         }
475         break;
476     case STATS_DERIVATIVE:
477     {
478         counter_sample_t *p_new, *p_old;
479         if( mdate() - p_counter->last_update < p_counter->update_interval )
480         {
481             return VLC_EGENERIC;
482         }
483         p_counter->last_update = mdate();
484         if( p_counter->i_type != VLC_VAR_FLOAT &&
485             p_counter->i_type != VLC_VAR_INTEGER )
486         {
487             msg_Err( p_handler, "Unable to compute DERIVATIVE for this type");
488             return VLC_EGENERIC;
489         }
490         /* Insert the new one at the beginning */
491         p_new = (counter_sample_t*)malloc( sizeof( counter_sample_t ) );
492         p_new->value = val;
493         p_new->date = p_counter->last_update;
494         INSERT_ELEM( p_counter->pp_samples, p_counter->i_samples,
495                      0, p_new );
496
497         if( p_counter->i_samples == 3 )
498         {
499             p_old = p_counter->pp_samples[2];
500             REMOVE_ELEM( p_counter->pp_samples, p_counter->i_samples, 2 );
501             free( p_old );
502         }
503         break;
504     }
505     case STATS_COUNTER:
506         if( p_counter->i_samples > 1)
507         {
508             msg_Err( p_handler, "LAST counter has several samples !" );
509             return VLC_EGENERIC;
510         }
511         if( p_counter->i_samples == 0 )
512         {
513             counter_sample_t *p_new = (counter_sample_t*)malloc(
514                                                sizeof( counter_sample_t ) );
515             p_new->value.psz_string = NULL;
516
517             INSERT_ELEM( p_counter->pp_samples, p_counter->i_samples,
518                          p_counter->i_samples, p_new );
519         }
520         if( p_counter->i_samples == 1 )
521         {
522             switch( p_counter->i_type )
523             {
524             case VLC_VAR_INTEGER:
525                 p_counter->pp_samples[0]->value.i_int += val.i_int;
526                 if( new_val )
527                     new_val->i_int = p_counter->pp_samples[0]->value.i_int;
528                 break;
529             case VLC_VAR_FLOAT:
530                 p_counter->pp_samples[0]->value.f_float += val.f_float;
531                 if( new_val )
532                     new_val->f_float = p_counter->pp_samples[0]->value.f_float;
533             default:
534                 msg_Err( p_handler, "Trying to increment invalid variable %s",
535                          p_counter->psz_name );
536                 return VLC_EGENERIC;
537             }
538         }
539         break;
540     }
541     return VLC_SUCCESS;
542 }
543
544 static void TimerDump( vlc_object_t *p_obj, counter_t *p_counter,
545                        vlc_bool_t b_total )
546 {
547     mtime_t last, total;
548     int i_total;
549     if( !p_counter || p_counter->i_samples != 2 )
550     {
551         msg_Err( p_obj, "timer %s does not exist", p_counter->psz_name );
552         return;
553     }
554     i_total = p_counter->pp_samples[1]->value.i_int;
555     total = p_counter->pp_samples[1]->date;
556     if( p_counter->pp_samples[0]->value.b_bool == VLC_TRUE )
557     {
558         last = mdate() - p_counter->pp_samples[0]->date;
559         i_total += 1;
560         total += last;
561     }
562     else
563     {
564         last = p_counter->pp_samples[0]->date;
565     }
566     if( b_total )
567     {
568         msg_Dbg( p_obj,
569              "TIMER %s : %.3f ms - Total %.3f ms / %i intvls (Avg %.3f ms)",
570              p_counter->psz_name, (float)last/1000, (float)total/1000, i_total,
571              (float)(total)/(1000*(float)i_total ) );
572     }
573     else
574     {
575         msg_Dbg( p_obj,
576              "TIMER %s : Total %.3f ms / %i intvls (Avg %.3f ms)",
577              p_counter->psz_name, (float)total/1000, i_total,
578              (float)(total)/(1000*(float)i_total ) );
579     }
580 }