stats.c 24.7 KB
Newer Older
1 2 3
/*****************************************************************************
 * stats.c: Statistics handling
 *****************************************************************************
4 5
 * Copyright (C) 2006 the VideoLAN team
 * $Id$
6
 *
7
 * Authors: Clément Stenac <zorglub@videolan.org>
8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
Antoine Cellerier's avatar
Antoine Cellerier committed
21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 23 24 25 26 27 28 29
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdio.h>                                               /* required */

#include <vlc/vlc.h>
Clément Stenac's avatar
Clément Stenac committed
30
#include <vlc/input.h>
31 32 33 34

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
35
static counter_t *GetCounter( stats_handler_t *p_handler, int i_object_id,
36
                              unsigned int i_counter );
37 38
static int stats_CounterUpdate( stats_handler_t *p_handler,
                                counter_t *p_counter,
39
                                vlc_value_t val, vlc_value_t * );
40 41 42
static stats_handler_t* stats_HandlerCreate( vlc_object_t *p_this );
static stats_handler_t *stats_HandlerGet( vlc_object_t *p_this );

43 44
static void TimerDump( vlc_object_t *p_this, counter_t *p_counter, vlc_bool_t);

45 46 47 48
/*****************************************************************************
 * Exported functions
 *****************************************************************************/

Clément Stenac's avatar
Clément Stenac committed
49 50 51 52 53 54 55 56 57 58 59
/**
 * Cleanup statistics handler stuff
 * \param p_stats the handler to clean
 * \return nothing
 */
void stats_HandlerDestroy( stats_handler_t *p_stats )
{
    int i;
    for ( i =  p_stats->i_counters - 1 ; i >= 0 ; i-- )
    {
        int j;
60
        counter_t *p_counter = p_stats->pp_counters[i];
Clément Stenac's avatar
Clément Stenac committed
61 62 63 64 65 66 67 68

        for( j = p_counter->i_samples -1; j >= 0 ; j-- )
        {
            counter_sample_t *p_sample = p_counter->pp_samples[j];
            REMOVE_ELEM( p_counter->pp_samples, p_counter->i_samples, j );
            free( p_sample );
        }
        free( p_counter->psz_name );
69
        REMOVE_ELEM( p_stats->pp_counters, p_stats->i_counters, i );
Clément Stenac's avatar
Clément Stenac committed
70 71 72 73
        free( p_counter );
    }
}

74 75 76 77 78 79 80 81 82 83 84
/**
 * Create a statistics counter
 * \param p_this the object for which to create the counter
 * \param psz_name the name
 * \param i_type the type of stored data. One of VLC_VAR_STRING,
 * VLC_VAR_INTEGER, VLC_VAR_FLOAT
 * \param i_compute_type the aggregation type. One of STATS_LAST (always
 * keep the last value), STATS_COUNTER (increment by the passed value),
 * STATS_MAX (keep the maximum passed value), STATS_MIN, or STATS_DERIVATIVE
 * (keep a time derivative of the value)
 */
85 86
int __stats_Create( vlc_object_t *p_this, const char *psz_name, unsigned int i_id,
                    int i_type, int i_compute_type )
87 88
{
    counter_t *p_counter;
89 90 91 92 93 94 95
    stats_handler_t *p_handler;

    if( p_this->p_libvlc->b_stats == VLC_FALSE )
    {
        return VLC_EGENERIC;
    }
    p_handler = stats_HandlerGet( p_this );
96 97 98
    if( !p_handler ) return VLC_ENOMEM;

    vlc_mutex_lock( &p_handler->object_lock );
99 100 101 102

    p_counter = (counter_t*) malloc( sizeof( counter_t ) ) ;

    p_counter->psz_name = strdup( psz_name );
103
    p_counter->i_index = ((uint64_t)p_this->i_object_id << 32 ) + i_id;
104 105 106 107 108
    p_counter->i_compute_type = i_compute_type;
    p_counter->i_type = i_type;
    p_counter->i_samples = 0;
    p_counter->pp_samples = NULL;

109 110 111
    p_counter->update_interval = 0;
    p_counter->last_update = 0;

112 113
    INSERT_ELEM( p_handler->pp_counters, p_handler->i_counters,
                 p_handler->i_counters, p_counter );
114

115 116
    vlc_mutex_unlock( &p_handler->object_lock );

117 118 119
    return VLC_SUCCESS;
}

120 121 122 123 124 125
/** Update a counter element with new values
 * \param p_this the object in which to update
 * \param psz_name the name
 * \param val the vlc_value union containing the new value to aggregate. For
 * more information on how data is aggregated, \see __stats_Create
 */
126
int __stats_Update( vlc_object_t *p_this, unsigned int i_counter,
127
                    vlc_value_t val, vlc_value_t *val_new )
128
{
129
    int i_ret;
130 131 132
    counter_t *p_counter;

    /* Get stats handler singleton */
133 134 135 136 137 138
    stats_handler_t *p_handler;
    if( p_this->p_libvlc->b_stats == VLC_FALSE )
    {
        return VLC_EGENERIC;
    }
    p_handler = stats_HandlerGet( p_this );
139 140
    if( !p_handler ) return VLC_ENOMEM;

141
    vlc_mutex_lock( &p_handler->object_lock );
142
    /* Look for existing element */
143
    p_counter = GetCounter( p_handler, p_this->i_object_id, i_counter );
144 145
    if( !p_counter )
    {
146
        vlc_mutex_unlock( &p_handler->object_lock );
147 148 149 150
        vlc_object_release( p_handler );
        return VLC_ENOOBJ;
    }

151
    i_ret = stats_CounterUpdate( p_handler, p_counter, val, val_new );
152 153 154 155 156
    vlc_mutex_unlock( &p_handler->object_lock );

    return i_ret;
}

157 158 159 160 161 162 163 164
/** Get the aggregated value for a counter
 * \param p_this an object
 * \param i_object_id the object id from which we want the data
 * \param psz_name the name of the couner
 * \param val a pointer to an initialized vlc_value union. It will contain the
 * retrieved value
 * \return an error code
 */
165
int __stats_Get( vlc_object_t *p_this, int i_object_id,
166
                 unsigned int i_counter, vlc_value_t *val )
167 168 169 170
{
    counter_t *p_counter;

    /* Get stats handler singleton */
171 172 173 174 175 176
    stats_handler_t *p_handler;
    if( p_this->p_libvlc->b_stats == VLC_FALSE )
    {
        return VLC_EGENERIC;
    }
    p_handler = stats_HandlerGet( p_this );
177 178 179 180
    if( !p_handler ) return VLC_ENOMEM;
    vlc_mutex_lock( &p_handler->object_lock );

    /* Look for existing element */
181
    p_counter = GetCounter( p_handler, i_object_id, i_counter );
182 183 184 185
    if( !p_counter )
    {
        vlc_mutex_unlock( &p_handler->object_lock );
        vlc_object_release( p_handler );
186
        val->i_int = val->f_float = 0.0;
187 188 189 190 191 192
        return VLC_ENOOBJ;
    }

    if( p_counter->i_samples == 0 )
    {
        vlc_mutex_unlock( &p_handler->object_lock );
193
        val->i_int = val->f_float = 0.0;
194 195 196
        return VLC_EGENERIC;
    }

197 198 199 200 201 202 203 204 205
    switch( p_counter->i_compute_type )
    {
    case STATS_LAST:
    case STATS_MIN:
    case STATS_MAX:
    case STATS_COUNTER:
        *val = p_counter->pp_samples[0]->value;
        break;
    case STATS_DERIVATIVE:
Clément Stenac's avatar
Clément Stenac committed
206 207 208 209 210 211 212 213
        /* Not ready yet */
        if( p_counter->i_samples < 2 )
        {
            vlc_mutex_unlock( &p_handler->object_lock );
            val->i_int = 0; val->f_float = 0.0;
            return VLC_EGENERIC;
        }
        if( p_counter->i_type == VLC_VAR_INTEGER )
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
        {
            float f = ( p_counter->pp_samples[0]->value.i_int -
                        p_counter->pp_samples[1]->value.i_int ) /
                    (float)(  p_counter->pp_samples[0]->date -
                              p_counter->pp_samples[1]->date );
            val->i_int = (int)f;
        }
        else
        {
            float f = (float)( p_counter->pp_samples[0]->value.f_float -
                               p_counter->pp_samples[1]->value.f_float ) /
                      (float)( p_counter->pp_samples[0]->date -
                               p_counter->pp_samples[1]->date );
            val->f_float = f;
        }
        break;
    }
231 232 233 234
    vlc_object_release( p_handler );

    vlc_mutex_unlock( &p_handler->object_lock );
    return VLC_SUCCESS;;
235 236
}

237 238 239 240 241 242 243
/** Get a statistics counter structure. This allows for low-level modifications
 * \param p_this a parent object
 * \param i_object_id the object from which to retrieve data
 * \param psz_name the name
 * \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,
244
                               unsigned int i_counter )
245 246 247
{
    counter_t *p_counter;

248 249 250 251 252 253
    stats_handler_t *p_handler;
    if( p_this->p_libvlc->b_stats == VLC_FALSE )
    {
        return NULL;
    }
    p_handler = stats_HandlerGet( p_this );
254 255 256 257 258
    if( !p_handler ) return NULL;

    vlc_mutex_lock( &p_handler->object_lock );

    /* Look for existing element */
259
    p_counter = GetCounter( p_handler, i_object_id, i_counter );
260 261 262 263 264 265 266
    vlc_mutex_unlock( &p_handler->object_lock );
    vlc_object_release( p_handler );

    return p_counter;
}


267 268 269
void stats_ComputeInputStats( input_thread_t *p_input,
                              input_stats_t *p_stats )
{
270 271 272
    vlc_object_t *p_obj;
    vlc_list_t *p_list;
    int i_index;
273
    vlc_mutex_lock( &p_stats->lock );
274 275

    /* Input */
276
    stats_GetInteger( p_input, p_input->i_object_id, STATS_READ_PACKETS,
277
                       &p_stats->i_read_packets );
278
    stats_GetInteger( p_input, p_input->i_object_id, STATS_READ_BYTES,
279
                       &p_stats->i_read_bytes );
280
    stats_GetFloat( p_input, p_input->i_object_id, STATS_INPUT_BITRATE,
281 282
                       &p_stats->f_input_bitrate );

283
    stats_GetInteger( p_input, p_input->i_object_id, STATS_DEMUX_READ,
284
                      &p_stats->i_demux_read_bytes );
285
    stats_GetFloat( p_input, p_input->i_object_id, STATS_DEMUX_BITRATE,
286 287
                      &p_stats->f_demux_bitrate );

288
    stats_GetInteger( p_input, p_input->i_object_id, STATS_DECODED_VIDEO,
289
                      &p_stats->i_decoded_video );
290
    stats_GetInteger( p_input, p_input->i_object_id, STATS_DECODED_AUDIO,
291 292
                      &p_stats->i_decoded_audio );

293
    /* Sout */
294
    stats_GetInteger( p_input, p_input->i_object_id, STATS_SOUT_SENT_PACKETS,
295
                      &p_stats->i_sent_packets );
296
    stats_GetInteger( p_input, p_input->i_object_id, STATS_SOUT_SENT_BYTES,
297
                      &p_stats->i_sent_bytes );
298
    stats_GetFloat  ( p_input, p_input->i_object_id, STATS_SOUT_SEND_BITRATE,
299 300
                      &p_stats->f_send_bitrate );

Clément Stenac's avatar
Clément Stenac committed
301
    /* Aout - We store in p_input because aout is shared */
302
    stats_GetInteger( p_input, p_input->i_object_id, STATS_PLAYED_ABUFFERS,
Clément Stenac's avatar
Clément Stenac committed
303
                      &p_stats->i_played_abuffers );
304
    stats_GetInteger( p_input, p_input->i_object_id, STATS_LOST_ABUFFERS,
Clément Stenac's avatar
Clément Stenac committed
305 306
                      &p_stats->i_lost_abuffers );

307
    /* Vouts - FIXME: Store all in input */
308 309 310 311 312 313 314 315 316
    p_list = vlc_list_find( p_input, VLC_OBJECT_VOUT, FIND_CHILD );
    if( p_list )
    {
        p_stats->i_displayed_pictures  = 0 ;
        p_stats->i_lost_pictures = 0;
        for( i_index = 0; i_index < p_list->i_count ; i_index ++ )
        {
            int i_displayed = 0, i_lost = 0;
            p_obj = (vlc_object_t *)p_list->p_values[i_index].p_object;
317 318
            stats_GetInteger( p_obj, p_obj->i_object_id,
                              STATS_DISPLAYED_PICTURES,
319
                              &i_displayed );
320
            stats_GetInteger( p_obj, p_obj->i_object_id, STATS_LOST_PICTURES,
321 322 323 324 325 326
                              &i_lost );
            p_stats->i_displayed_pictures += i_displayed;
            p_stats->i_lost_pictures += i_lost;
         }
        vlc_list_release( p_list );
    }
Clément Stenac's avatar
Clément Stenac committed
327

328 329 330
    vlc_mutex_unlock( &p_stats->lock );
}

331

332 333 334
void stats_ReinitInputStats( input_stats_t *p_stats )
{
    p_stats->i_read_packets = p_stats->i_read_bytes =
335 336 337 338
    p_stats->f_input_bitrate = p_stats->f_average_input_bitrate =
    p_stats->i_demux_read_packets = p_stats->i_demux_read_bytes =
    p_stats->f_demux_bitrate = p_stats->f_average_demux_bitrate =
    p_stats->i_displayed_pictures = p_stats->i_lost_pictures =
Clément Stenac's avatar
Clément Stenac committed
339
    p_stats->i_played_abuffers = p_stats->i_lost_abuffers =
340 341 342
    p_stats->i_decoded_video = p_stats->i_decoded_audio =
    p_stats->i_sent_bytes = p_stats->i_sent_packets = p_stats->f_send_bitrate
     = 0;
343 344 345 346 347
}

void stats_DumpInputStats( input_stats_t *p_stats  )
{
    vlc_mutex_lock( &p_stats->lock );
348 349
    /* f_bitrate is in bytes / microsecond
     * *1000 => bytes / millisecond => kbytes / seconds */
350 351 352
    fprintf( stderr, "Input : %i (%i bytes) - %f kB/s - "
                     "Demux : %i (%i bytes) - %f kB/s\n"
                     " - Vout : %i/%i - Aout : %i/%i - Sout : %f\n",
353
                    p_stats->i_read_packets, p_stats->i_read_bytes,
354
                    p_stats->f_input_bitrate * 1000,
355 356
                    p_stats->i_demux_read_packets, p_stats->i_demux_read_bytes,
                    p_stats->f_demux_bitrate * 1000,
Clément Stenac's avatar
Clément Stenac committed
357
                    p_stats->i_displayed_pictures, p_stats->i_lost_pictures,
358 359
                    p_stats->i_played_abuffers, p_stats->i_lost_abuffers,
                    p_stats->f_send_bitrate );
360 361 362
    vlc_mutex_unlock( &p_stats->lock );
}

363 364 365 366 367 368 369
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 );

370
    p_list = vlc_list_find( p_obj, VLC_OBJECT_INPUT, FIND_ANYWHERE );
371 372
    if( p_list )
    {
373
        float f_total_in = 0, f_total_out = 0,f_total_demux = 0;
374 375
        for( i_index = 0; i_index < p_list->i_count ; i_index ++ )
        {
376
            float f_in = 0, f_out = 0, f_demux = 0;
377
            p_obj = (vlc_object_t *)p_list->p_values[i_index].p_object;
378
            stats_GetFloat( p_obj, p_obj->i_object_id, STATS_INPUT_BITRATE,
379
                            &f_in );
380
            stats_GetFloat( p_obj, p_obj->i_object_id, STATS_SOUT_SEND_BITRATE,
381
                            &f_out );
382
            stats_GetFloat( p_obj, p_obj->i_object_id, STATS_DEMUX_BITRATE,
383 384
                            &f_demux );
            f_total_in += f_in; f_total_out += f_out;f_total_demux += f_demux;
385
        }
386 387 388
        p_stats->f_input_bitrate = f_total_in;
        p_stats->f_output_bitrate = f_total_out;
        p_stats->f_demux_bitrate = f_total_demux;
389 390 391 392 393 394 395 396 397 398 399 400
        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;
}


401 402
void __stats_TimerStart( vlc_object_t *p_obj, const char *psz_name,
                         unsigned int i_id )
403 404
{
    counter_t *p_counter = stats_CounterGet( p_obj,
405
                                             p_obj->p_vlc->i_object_id, i_id );
406 407 408
    if( !p_counter )
    {
        counter_sample_t *p_sample;
409
        stats_Create( p_obj->p_vlc, psz_name, i_id, VLC_VAR_TIME, STATS_TIMER );
410
        p_counter = stats_CounterGet( p_obj,  p_obj->p_vlc->i_object_id,
411
                                      i_id );
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
        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();
}

433
void __stats_TimerStop( vlc_object_t *p_obj, unsigned int i_id )
434 435
{
    counter_t *p_counter = stats_CounterGet( p_obj,
436 437
                                             p_obj->p_vlc->i_object_id,
                                             i_id );
438 439
    if( !p_counter || p_counter->i_samples != 2 )
    {
440
        msg_Err( p_obj, "timer does not exist" );
441 442 443 444 445 446 447 448
        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;
}

449
void __stats_TimerDump( vlc_object_t *p_obj, unsigned int i_id )
450 451 452
{
    counter_t *p_counter = stats_CounterGet( p_obj,
                                             p_obj->p_vlc->i_object_id,
453
                                             i_id );
454
    TimerDump( p_obj, p_counter, VLC_TRUE );
455 456
}

457

458 459
void __stats_TimersDumpAll( vlc_object_t *p_obj )
{
460 461 462
    int i;
    stats_handler_t *p_handler = stats_HandlerGet( p_obj );
    if( !p_handler ) return;
463

464 465 466
    vlc_mutex_lock( &p_handler->object_lock );
    for ( i = 0 ; i< p_handler->i_counters; i++ )
    {
467
        counter_t * p_counter = p_handler->pp_counters[i];
468
        if( p_counter->i_compute_type == STATS_TIMER )
469
        {
470
            TimerDump( p_obj, p_counter, VLC_FALSE );
471 472 473
        }
    }
    vlc_mutex_unlock( &p_handler->object_lock );
474 475
}

476 477 478 479 480 481 482 483 484 485 486 487 488 489

/********************************************************************
 * Following functions are local
 ********************************************************************/

/**
 * Update a statistics counter, according to its type
 * If needed, perform a bit of computation (derivative, mostly)
 * This function must be entered with stats handler lock
 * \param p_handler stats handler singleton
 * \param p_counter the counter to update
 * \param val the "new" value
 * \return an error code
 */
490 491
static int stats_CounterUpdate( stats_handler_t *p_handler,
                                counter_t *p_counter,
492
                                vlc_value_t val, vlc_value_t *new_val )
493 494 495
{
    switch( p_counter->i_compute_type )
    {
496 497
    case STATS_LAST:
    case STATS_MIN:
Clément Stenac's avatar
Clément Stenac committed
498
    case STATS_MAX:
499 500 501 502 503
        if( p_counter->i_samples > 1)
        {
            msg_Err( p_handler, "LAST counter has several samples !" );
            return VLC_EGENERIC;
        }
504 505 506 507
        if( p_counter->i_type != VLC_VAR_FLOAT &&
            p_counter->i_type != VLC_VAR_INTEGER &&
            p_counter->i_compute_type != STATS_LAST )
        {
508
            msg_Err( p_handler, "unable to compute MIN or MAX for this type");
509 510 511
            return VLC_EGENERIC;
        }

512 513 514 515 516 517 518 519 520 521 522
        if( p_counter->i_samples == 0 )
        {
            counter_sample_t *p_new = (counter_sample_t*)malloc(
                                               sizeof( counter_sample_t ) );
            p_new->value.psz_string = NULL;

            INSERT_ELEM( p_counter->pp_samples, p_counter->i_samples,
                         p_counter->i_samples, p_new );
        }
        if( p_counter->i_samples == 1 )
        {
523 524 525 526 527 528 529 530 531 532 533 534 535 536
            /* Update if : LAST or (MAX and bigger) or (MIN and bigger) */
            if( p_counter->i_compute_type == STATS_LAST ||
                ( p_counter->i_compute_type == STATS_MAX &&
                   ( ( p_counter->i_type == VLC_VAR_INTEGER &&
                       p_counter->pp_samples[0]->value.i_int > val.i_int ) ||
                     ( p_counter->i_type == VLC_VAR_FLOAT &&
                       p_counter->pp_samples[0]->value.f_float > val.f_float )
                   ) ) ||
                ( p_counter->i_compute_type == STATS_MIN &&
                   ( ( p_counter->i_type == VLC_VAR_INTEGER &&
                       p_counter->pp_samples[0]->value.i_int < val.i_int ) ||
                     ( p_counter->i_type == VLC_VAR_FLOAT &&
                       p_counter->pp_samples[0]->value.f_float < val.f_float )
                   ) ) )
537
            {
538 539 540 541 542 543
                if( p_counter->i_type == VLC_VAR_STRING &&
                    p_counter->pp_samples[0]->value.psz_string )
                {
                    free( p_counter->pp_samples[0]->value.psz_string );
                }
                p_counter->pp_samples[0]->value = val;
544
                *new_val = p_counter->pp_samples[0]->value;
545 546 547
            }
        }
        break;
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576
    case STATS_DERIVATIVE:
    {
        counter_sample_t *p_new, *p_old;
        if( mdate() - p_counter->last_update < p_counter->update_interval )
        {
            return VLC_EGENERIC;
        }
        p_counter->last_update = mdate();
        if( p_counter->i_type != VLC_VAR_FLOAT &&
            p_counter->i_type != VLC_VAR_INTEGER )
        {
            msg_Err( p_handler, "Unable to compute DERIVATIVE for this type");
            return VLC_EGENERIC;
        }
        /* Insert the new one at the beginning */
        p_new = (counter_sample_t*)malloc( sizeof( counter_sample_t ) );
        p_new->value = val;
        p_new->date = p_counter->last_update;
        INSERT_ELEM( p_counter->pp_samples, p_counter->i_samples,
                     0, p_new );

        if( p_counter->i_samples == 3 )
        {
            p_old = p_counter->pp_samples[2];
            REMOVE_ELEM( p_counter->pp_samples, p_counter->i_samples, 2 );
            free( p_old );
        }
        break;
    }
577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597
    case STATS_COUNTER:
        if( p_counter->i_samples > 1)
        {
            msg_Err( p_handler, "LAST counter has several samples !" );
            return VLC_EGENERIC;
        }
        if( p_counter->i_samples == 0 )
        {
            counter_sample_t *p_new = (counter_sample_t*)malloc(
                                               sizeof( counter_sample_t ) );
            p_new->value.psz_string = NULL;

            INSERT_ELEM( p_counter->pp_samples, p_counter->i_samples,
                         p_counter->i_samples, p_new );
        }
        if( p_counter->i_samples == 1 )
        {
            switch( p_counter->i_type )
            {
            case VLC_VAR_INTEGER:
                p_counter->pp_samples[0]->value.i_int += val.i_int;
598 599
                if( new_val )
                    new_val->i_int = p_counter->pp_samples[0]->value.i_int;
600
                break;
601 602 603 604
            case VLC_VAR_FLOAT:
                p_counter->pp_samples[0]->value.f_float += val.f_float;
                if( new_val )
                    new_val->f_float = p_counter->pp_samples[0]->value.f_float;
605 606 607 608 609 610 611 612 613 614 615
            default:
                msg_Err( p_handler, "Trying to increment invalid variable %s",
                         p_counter->psz_name );
                return VLC_EGENERIC;
            }
        }
        break;
    }
    return VLC_SUCCESS;
}

616
static counter_t *GetCounter( stats_handler_t *p_handler, int i_object_id,
617
                              unsigned int i_counter )
618 619
{
    int i;
620 621 622 623 624 625 626
    uint64_t i_index = ((uint64_t) i_object_id << 32 ) + i_counter;
    for (i = 0 ; i < p_handler->i_counters ; i++ )
    {
         if( i_index == p_handler->pp_counters[i]->i_index )
             return p_handler->pp_counters[i];
    }
    return NULL;
627 628
}

629

630 631
static stats_handler_t *stats_HandlerGet( vlc_object_t *p_this )
{
632
    stats_handler_t *p_handler = p_this->p_libvlc->p_stats;
633 634 635 636 637 638 639 640
    if( !p_handler )
    {
        p_handler = stats_HandlerCreate( p_this );
        if( !p_handler )
        {
            return NULL;
        }
    }
641
    vlc_object_yield( p_handler );
642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665
    return p_handler;
}

/**
 * Initialize statistics handler
 *
 * This function initializes the global statistics handler singleton,
 * \param p_this the parent VLC object
 */
static stats_handler_t* stats_HandlerCreate( vlc_object_t *p_this )
{
    stats_handler_t *p_handler;

    msg_Dbg( p_this, "creating statistics handler" );

    p_handler = (stats_handler_t*) vlc_object_create( p_this,
                                                      VLC_OBJECT_STATS );

    if( !p_handler )
    {
        msg_Err( p_this, "out of memory" );
        return NULL;
    }
    p_handler->i_counters = 0;
666
    p_handler->pp_counters = NULL;
667 668 669 670

    /// \bug is it p_vlc or p_libvlc ?
    vlc_object_attach( p_handler, p_this->p_vlc );

671 672
    p_this->p_libvlc->p_stats = p_handler;

673 674
    return p_handler;
}
675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712

static void TimerDump( vlc_object_t *p_obj, counter_t *p_counter,
                       vlc_bool_t b_total )
{
    mtime_t last, total;
    int i_total;
    if( !p_counter || p_counter->i_samples != 2 )
    {
        msg_Err( p_obj, "timer %s does not exist", p_counter->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;
    }
    if( b_total )
    {
        msg_Dbg( p_obj,
             "TIMER %s : %.3f ms - Total %.3f ms / %i intvls (Avg %.3f ms)",
             p_counter->psz_name, (float)last/1000, (float)total/1000, i_total,
             (float)(total)/(1000*(float)i_total ) );
    }
    else
    {
        msg_Dbg( p_obj,
             "TIMER %s : Total %.3f ms / %i intvls (Avg %.3f ms)",
             p_counter->psz_name, (float)total/1000, i_total,
             (float)(total)/(1000*(float)i_total ) );
    }
}