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