]> git.sesse.net Git - vlc/blob - src/misc/stats.c
d4f0beae06c96db4b3eb20c8c5229a0fff3bd88a
[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->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->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->b_stats ) return;
139
140     vlc_mutex_lock( &p_stats->lock );
141
142     /* Input */
143     stats_GetInteger( p_input, p_input->counters.p_read_packets,
144                       &p_stats->i_read_packets );
145     stats_GetInteger( p_input, p_input->counters.p_read_bytes,
146                       &p_stats->i_read_bytes );
147     stats_GetFloat( p_input, p_input->counters.p_input_bitrate,
148                     &p_stats->f_input_bitrate );
149     stats_GetInteger( p_input, p_input->counters.p_demux_read,
150                       &p_stats->i_demux_read_bytes );
151     stats_GetFloat( p_input, p_input->counters.p_demux_bitrate,
152                     &p_stats->f_demux_bitrate );
153
154     /* Decoders */
155     stats_GetInteger( p_input, p_input->counters.p_decoded_video,
156                       &p_stats->i_decoded_video );
157     stats_GetInteger( p_input, p_input->counters.p_decoded_audio,
158                       &p_stats->i_decoded_audio );
159
160     /* Sout */
161     if( p_input->counters.p_sout_send_bitrate )
162     {
163         stats_GetInteger( p_input, p_input->counters.p_sout_sent_packets,
164                           &p_stats->i_sent_packets );
165         stats_GetInteger( p_input, p_input->counters.p_sout_sent_bytes,
166                           &p_stats->i_sent_bytes );
167         stats_GetFloat  ( p_input, p_input->counters.p_sout_send_bitrate,
168                           &p_stats->f_send_bitrate );
169     }
170
171     /* Aout */
172     stats_GetInteger( p_input, p_input->counters.p_played_abuffers,
173                       &p_stats->i_played_abuffers );
174     stats_GetInteger( p_input, p_input->counters.p_lost_abuffers,
175                       &p_stats->i_lost_abuffers );
176
177     /* Vouts */
178     stats_GetInteger( p_input, p_input->counters.p_displayed_pictures,
179                       &p_stats->i_displayed_pictures );
180     stats_GetInteger( p_input, p_input->counters.p_lost_pictures,
181                       &p_stats->i_lost_pictures );
182
183     vlc_mutex_unlock( &p_stats->lock );
184 }
185
186 void stats_ReinitInputStats( input_stats_t *p_stats )
187 {
188     p_stats->i_read_packets = p_stats->i_read_bytes =
189     p_stats->f_input_bitrate = p_stats->f_average_input_bitrate =
190     p_stats->i_demux_read_packets = p_stats->i_demux_read_bytes =
191     p_stats->f_demux_bitrate = p_stats->f_average_demux_bitrate =
192     p_stats->i_displayed_pictures = p_stats->i_lost_pictures =
193     p_stats->i_played_abuffers = p_stats->i_lost_abuffers =
194     p_stats->i_decoded_video = p_stats->i_decoded_audio =
195     p_stats->i_sent_bytes = p_stats->i_sent_packets = p_stats->f_send_bitrate
196      = 0;
197 }
198
199 void stats_DumpInputStats( input_stats_t *p_stats  )
200 {
201     vlc_mutex_lock( &p_stats->lock );
202     /* f_bitrate is in bytes / microsecond
203      * *1000 => bytes / millisecond => kbytes / seconds */
204     fprintf( stderr, "Input : %i (%i bytes) - %f kB/s - "
205                      "Demux : %i (%i bytes) - %f kB/s\n"
206                      " - Vout : %i/%i - Aout : %i/%i - Sout : %f\n",
207                     p_stats->i_read_packets, p_stats->i_read_bytes,
208                     p_stats->f_input_bitrate * 1000,
209                     p_stats->i_demux_read_packets, p_stats->i_demux_read_bytes,
210                     p_stats->f_demux_bitrate * 1000,
211                     p_stats->i_displayed_pictures, p_stats->i_lost_pictures,
212                     p_stats->i_played_abuffers, p_stats->i_lost_abuffers,
213                     p_stats->f_send_bitrate );
214     vlc_mutex_unlock( &p_stats->lock );
215 }
216
217 void __stats_ComputeGlobalStats( vlc_object_t *p_obj, global_stats_t *p_stats )
218 {
219     vlc_list_t *p_list;
220     int i_index;
221
222     if( !p_obj->p_libvlc->b_stats ) return;
223
224     vlc_mutex_lock( &p_stats->lock );
225
226     p_list = vlc_list_find( p_obj, VLC_OBJECT_INPUT, FIND_ANYWHERE );
227     if( p_list )
228     {
229         float f_total_in = 0, f_total_out = 0,f_total_demux = 0;
230         for( i_index = 0; i_index < p_list->i_count ; i_index ++ )
231         {
232             float f_in = 0, f_out = 0, f_demux = 0;
233             input_thread_t *p_input = (input_thread_t *)
234                              p_list->p_values[i_index].p_object;
235             stats_GetFloat( p_obj, p_input->counters.p_input_bitrate, &f_in );
236             if( p_input->counters.p_sout_send_bitrate )
237                 stats_GetFloat( p_obj, p_input->counters.p_sout_send_bitrate,
238                                     &f_out );
239             stats_GetFloat( p_obj, p_input->counters.p_demux_bitrate,
240                                 &f_demux );
241             f_total_in += f_in; f_total_out += f_out;f_total_demux += f_demux;
242         }
243         p_stats->f_input_bitrate = f_total_in;
244         p_stats->f_output_bitrate = f_total_out;
245         p_stats->f_demux_bitrate = f_total_demux;
246         vlc_list_release( p_list );
247     }
248
249     vlc_mutex_unlock( &p_stats->lock );
250 }
251
252 void stats_ReinitGlobalStats( global_stats_t *p_stats )
253 {
254     p_stats->f_input_bitrate = p_stats->f_output_bitrate = 0.0;
255 }
256
257
258 void __stats_TimerStart( vlc_object_t *p_obj, const char *psz_name,
259                          unsigned int i_id )
260 {
261     int i;
262     counter_t *p_counter = NULL;
263     if( !p_obj->p_libvlc->b_stats ) return;
264     vlc_mutex_lock( &p_obj->p_libvlc->timer_lock );
265
266     for( i = 0 ; i < p_obj->p_libvlc->i_timers; i++ )
267     {
268         if( p_obj->p_libvlc->pp_timers[i]->i_id == i_id )
269         {
270             p_counter = p_obj->p_libvlc->pp_timers[i];
271             break;
272         }
273     }
274     if( !p_counter )
275     {
276         counter_sample_t *p_sample;
277         p_counter = stats_CounterCreate( p_obj->p_libvlc, VLC_VAR_TIME,
278                                          STATS_TIMER );
279         if( !p_counter ) return;
280         p_counter->psz_name = strdup( psz_name );
281         p_counter->i_id = i_id;
282         INSERT_ELEM( p_obj->p_libvlc->pp_timers, p_obj->p_libvlc->i_timers,
283                      p_obj->p_libvlc->i_timers, p_counter );
284
285         /* 1st sample : if started: start_date, else last_time, b_started */
286         p_sample = (counter_sample_t *)malloc( sizeof( counter_sample_t ) );
287         INSERT_ELEM( p_counter->pp_samples, p_counter->i_samples,
288                      p_counter->i_samples, p_sample );
289         p_sample->date = 0; p_sample->value.b_bool = 0;
290         /* 2nd sample : global_time, i_samples */
291         p_sample = (counter_sample_t *)malloc( sizeof( counter_sample_t ) );
292         INSERT_ELEM( p_counter->pp_samples, p_counter->i_samples,
293                      p_counter->i_samples, p_sample );
294         p_sample->date = 0; p_sample->value.i_int = 0;
295     }
296     if( p_counter->pp_samples[0]->value.b_bool == VLC_TRUE )
297     {
298         msg_Warn( p_obj, "timer %s was already started !", psz_name );
299         return;
300     }
301     p_counter->pp_samples[0]->value.b_bool = VLC_TRUE;
302     p_counter->pp_samples[0]->date = mdate();
303     vlc_mutex_unlock( &p_obj->p_libvlc->timer_lock );
304 }
305
306 void __stats_TimerStop( vlc_object_t *p_obj, unsigned int i_id )
307 {
308     counter_t *p_counter = NULL;
309     int i;
310     if( !p_obj->p_libvlc->b_stats ) return;
311     vlc_mutex_lock( &p_obj->p_libvlc->timer_lock );
312     for( i = 0 ; i < p_obj->p_libvlc->i_timers; i++ )
313     {
314         if( p_obj->p_libvlc->pp_timers[i]->i_id == i_id )
315         {
316             p_counter = p_obj->p_libvlc->pp_timers[i];
317             break;
318         }
319     }
320     if( !p_counter || p_counter->i_samples != 2 )
321     {
322         msg_Err( p_obj, "timer does not exist" );
323         vlc_mutex_unlock( &p_obj->p_libvlc->timer_lock );
324         return;
325     }
326     p_counter->pp_samples[0]->value.b_bool = VLC_FALSE;
327     p_counter->pp_samples[1]->value.i_int += 1;
328     p_counter->pp_samples[0]->date = mdate() - p_counter->pp_samples[0]->date;
329     p_counter->pp_samples[1]->date += p_counter->pp_samples[0]->date;
330     vlc_mutex_unlock( &p_obj->p_libvlc->timer_lock );
331 }
332
333 void __stats_TimerDump( vlc_object_t *p_obj, unsigned int i_id )
334 {
335     counter_t *p_counter = NULL;
336     int i;
337     if( !p_obj->p_libvlc->b_stats ) return;
338     vlc_mutex_lock( &p_obj->p_libvlc->timer_lock );
339     for( i = 0 ; i < p_obj->p_libvlc->i_timers; i++ )
340     {
341         if( p_obj->p_libvlc->pp_timers[i]->i_id == i_id )
342         {
343             p_counter = p_obj->p_libvlc->pp_timers[i];
344             break;
345         }
346     }
347     TimerDump( p_obj, p_counter, VLC_TRUE );
348     vlc_mutex_unlock( &p_obj->p_libvlc->timer_lock );
349 }
350
351
352 void __stats_TimersDumpAll( vlc_object_t *p_obj )
353 {
354     int i;
355     if( !p_obj->p_libvlc->b_stats ) return;
356     vlc_mutex_lock( &p_obj->p_libvlc->timer_lock );
357     for ( i = 0 ; i< p_obj->p_libvlc->i_timers ; i++ )
358         TimerDump( p_obj, p_obj->p_libvlc->pp_timers[i], VLC_FALSE );
359     vlc_mutex_unlock( &p_obj->p_libvlc->timer_lock );
360 }
361
362 void __stats_TimersClean( vlc_object_t *p_obj )
363 {
364     int i;
365     vlc_mutex_lock( &p_obj->p_libvlc->timer_lock );
366     for ( i = p_obj->p_libvlc->i_timers -1 ; i >= 0; i-- )
367     {
368         counter_t *p_counter = p_obj->p_libvlc->pp_timers[i];
369         REMOVE_ELEM( p_obj->p_libvlc->pp_timers, p_obj->p_libvlc->i_timers, i );
370         stats_CounterClean( p_counter );
371     }
372     vlc_mutex_unlock( &p_obj->p_libvlc->timer_lock );
373 }
374
375 void stats_CounterClean( counter_t *p_c )
376 {
377     int i;
378     if( p_c )
379     {
380         i = p_c->i_samples - 1 ;
381         while( i >= 0)
382         {
383             counter_sample_t *p_s = p_c->pp_samples[i];
384             REMOVE_ELEM( p_c->pp_samples, p_c->i_samples, i );
385             free( p_s );
386             i--;
387         }
388     if( p_c->psz_name ) free( p_c->psz_name );
389     free( p_c );
390     }
391 }
392
393
394 /********************************************************************
395  * Following functions are local
396  ********************************************************************/
397
398 /**
399  * Update a statistics counter, according to its type
400  * If needed, perform a bit of computation (derivative, mostly)
401  * This function must be entered with stats handler lock
402  * \param p_counter the counter to update
403  * \param val the "new" value
404  * \return an error code
405  */
406 static int CounterUpdate( vlc_object_t *p_handler,
407                           counter_t *p_counter,
408                           vlc_value_t val, vlc_value_t *new_val )
409 {
410     switch( p_counter->i_compute_type )
411     {
412     case STATS_LAST:
413     case STATS_MIN:
414     case STATS_MAX:
415         if( p_counter->i_samples > 1)
416         {
417             msg_Err( p_handler, "LAST counter has several samples !" );
418             return VLC_EGENERIC;
419         }
420         if( p_counter->i_type != VLC_VAR_FLOAT &&
421             p_counter->i_type != VLC_VAR_INTEGER &&
422             p_counter->i_compute_type != STATS_LAST )
423         {
424             msg_Err( p_handler, "unable to compute MIN or MAX for this type");
425             return VLC_EGENERIC;
426         }
427
428         if( p_counter->i_samples == 0 )
429         {
430             counter_sample_t *p_new = (counter_sample_t*)malloc(
431                                                sizeof( counter_sample_t ) );
432             p_new->value.psz_string = NULL;
433
434             INSERT_ELEM( p_counter->pp_samples, p_counter->i_samples,
435                          p_counter->i_samples, p_new );
436         }
437         if( p_counter->i_samples == 1 )
438         {
439             /* Update if : LAST or (MAX and bigger) or (MIN and bigger) */
440             if( p_counter->i_compute_type == STATS_LAST ||
441                 ( p_counter->i_compute_type == STATS_MAX &&
442                    ( ( p_counter->i_type == VLC_VAR_INTEGER &&
443                        p_counter->pp_samples[0]->value.i_int > val.i_int ) ||
444                      ( p_counter->i_type == VLC_VAR_FLOAT &&
445                        p_counter->pp_samples[0]->value.f_float > val.f_float )
446                    ) ) ||
447                 ( p_counter->i_compute_type == STATS_MIN &&
448                    ( ( p_counter->i_type == VLC_VAR_INTEGER &&
449                        p_counter->pp_samples[0]->value.i_int < val.i_int ) ||
450                      ( p_counter->i_type == VLC_VAR_FLOAT &&
451                        p_counter->pp_samples[0]->value.f_float < val.f_float )
452                    ) ) )
453             {
454                 if( p_counter->i_type == VLC_VAR_STRING &&
455                     p_counter->pp_samples[0]->value.psz_string )
456                 {
457                     free( p_counter->pp_samples[0]->value.psz_string );
458                 }
459                 p_counter->pp_samples[0]->value = val;
460                 *new_val = p_counter->pp_samples[0]->value;
461             }
462         }
463         break;
464     case STATS_DERIVATIVE:
465     {
466         counter_sample_t *p_new, *p_old;
467         if( mdate() - p_counter->last_update < p_counter->update_interval )
468         {
469             return VLC_EGENERIC;
470         }
471         p_counter->last_update = mdate();
472         if( p_counter->i_type != VLC_VAR_FLOAT &&
473             p_counter->i_type != VLC_VAR_INTEGER )
474         {
475             msg_Err( p_handler, "Unable to compute DERIVATIVE for this type");
476             return VLC_EGENERIC;
477         }
478         /* Insert the new one at the beginning */
479         p_new = (counter_sample_t*)malloc( sizeof( counter_sample_t ) );
480         p_new->value = val;
481         p_new->date = p_counter->last_update;
482         INSERT_ELEM( p_counter->pp_samples, p_counter->i_samples,
483                      0, p_new );
484
485         if( p_counter->i_samples == 3 )
486         {
487             p_old = p_counter->pp_samples[2];
488             REMOVE_ELEM( p_counter->pp_samples, p_counter->i_samples, 2 );
489             free( p_old );
490         }
491         break;
492     }
493     case STATS_COUNTER:
494         if( p_counter->i_samples > 1)
495         {
496             msg_Err( p_handler, "LAST counter has several samples !" );
497             return VLC_EGENERIC;
498         }
499         if( p_counter->i_samples == 0 )
500         {
501             counter_sample_t *p_new = (counter_sample_t*)malloc(
502                                                sizeof( counter_sample_t ) );
503             p_new->value.psz_string = NULL;
504
505             INSERT_ELEM( p_counter->pp_samples, p_counter->i_samples,
506                          p_counter->i_samples, p_new );
507         }
508         if( p_counter->i_samples == 1 )
509         {
510             switch( p_counter->i_type )
511             {
512             case VLC_VAR_INTEGER:
513                 p_counter->pp_samples[0]->value.i_int += val.i_int;
514                 if( new_val )
515                     new_val->i_int = p_counter->pp_samples[0]->value.i_int;
516                 break;
517             case VLC_VAR_FLOAT:
518                 p_counter->pp_samples[0]->value.f_float += val.f_float;
519                 if( new_val )
520                     new_val->f_float = p_counter->pp_samples[0]->value.f_float;
521             default:
522                 msg_Err( p_handler, "Trying to increment invalid variable %s",
523                          p_counter->psz_name );
524                 return VLC_EGENERIC;
525             }
526         }
527         break;
528     }
529     return VLC_SUCCESS;
530 }
531
532 static void TimerDump( vlc_object_t *p_obj, counter_t *p_counter,
533                        vlc_bool_t b_total )
534 {
535     mtime_t last, total;
536     int i_total;
537     if( !p_counter || p_counter->i_samples != 2 )
538     {
539         msg_Err( p_obj, "timer %s does not exist", p_counter->psz_name );
540         return;
541     }
542     i_total = p_counter->pp_samples[1]->value.i_int;
543     total = p_counter->pp_samples[1]->date;
544     if( p_counter->pp_samples[0]->value.b_bool == VLC_TRUE )
545     {
546         last = mdate() - p_counter->pp_samples[0]->date;
547         i_total += 1;
548         total += last;
549     }
550     else
551     {
552         last = p_counter->pp_samples[0]->date;
553     }
554     if( b_total )
555     {
556         msg_Dbg( p_obj,
557              "TIMER %s : %.3f ms - Total %.3f ms / %i intvls (Avg %.3f ms)",
558              p_counter->psz_name, (float)last/1000, (float)total/1000, i_total,
559              (float)(total)/(1000*(float)i_total ) );
560     }
561     else
562     {
563         msg_Dbg( p_obj,
564              "TIMER %s : Total %.3f ms / %i intvls (Avg %.3f ms)",
565              p_counter->psz_name, (float)total/1000, i_total,
566              (float)(total)/(1000*(float)i_total ) );
567     }
568 }