stats.c 19 KB
Newer Older
1 2 3
/*****************************************************************************
 * stats.c: Statistics handling
 *****************************************************************************
4 5
 * Copyright (C) 2006 the VideoLAN team
 * $Id$
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * Authors: Clément Stenac <zorglub@videolan.org>
 *
 * 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 37 38 39 40 41 42 43 44 45 46
                            char *psz_name );
static int stats_CounterUpdate( stats_handler_t *p_handler,
                                counter_t *p_counter,
                                vlc_value_t val );
static stats_handler_t* stats_HandlerCreate( vlc_object_t *p_this );
static stats_handler_t *stats_HandlerGet( vlc_object_t *p_this );

/*****************************************************************************
 * Exported functions
 *****************************************************************************/

Clément Stenac's avatar
Clément Stenac committed
47 48 49 50 51 52 53 54 55 56 57
/**
 * 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;
Clément Stenac's avatar
Fix  
Clément Stenac committed
58
        counter_t * p_counter = p_stats->pp_counters[i];
Clément Stenac's avatar
Clément Stenac committed
59 60 61 62 63 64 65 66

        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 );
Clément Stenac's avatar
Fix  
Clément Stenac committed
67
        REMOVE_ELEM( p_stats->pp_counters, p_stats->i_counters, i );
Clément Stenac's avatar
Clément Stenac committed
68 69 70 71
        free( p_counter );
    }
}

72 73 74 75 76 77 78 79 80 81 82
/**
 * 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)
 */
83 84 85 86
int __stats_Create( vlc_object_t *p_this, char *psz_name, int i_type,
                    int i_compute_type )
{
    counter_t *p_counter;
87 88 89 90 91 92 93
    stats_handler_t *p_handler;

    if( p_this->p_libvlc->b_stats == VLC_FALSE )
    {
        return VLC_EGENERIC;
    }
    p_handler = stats_HandlerGet( p_this );
Clément Stenac's avatar
Clément Stenac committed
94 95 96
    if( !p_handler ) return VLC_ENOMEM;

    vlc_mutex_lock( &p_handler->object_lock );
97 98 99 100 101 102 103 104 105 106

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

    p_counter->psz_name = strdup( psz_name );
    p_counter->i_source_object = p_this->i_object_id;
    p_counter->i_compute_type = i_compute_type;
    p_counter->i_type = i_type;
    p_counter->i_samples = 0;
    p_counter->pp_samples = NULL;

107 108 109
    p_counter->update_interval = 0;
    p_counter->last_update = 0;

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

Clément Stenac's avatar
Clément Stenac committed
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 127
int __stats_Update( vlc_object_t *p_this, char *psz_name, vlc_value_t val )
{
Clément Stenac's avatar
Clément Stenac committed
128
    int i_ret;
129 130 131
    counter_t *p_counter;

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

Clément Stenac's avatar
Clément Stenac committed
140
    vlc_mutex_lock( &p_handler->object_lock );
141
    /* Look for existing element */
142 143
    p_counter = GetCounter( p_handler, p_this->i_object_id,
                            psz_name );
144 145
    if( !p_counter )
    {
Clément Stenac's avatar
Clément Stenac committed
146
        vlc_mutex_unlock( &p_handler->object_lock );
147 148 149 150
        vlc_object_release( p_handler );
        return VLC_ENOOBJ;
    }

Clément Stenac's avatar
Clément Stenac committed
151 152 153 154 155 156
    i_ret = stats_CounterUpdate( p_handler, p_counter, val );
    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
 */
Clément Stenac's avatar
Clément Stenac committed
165 166 167 168 169
int __stats_Get( vlc_object_t *p_this, int i_object_id, char *psz_name, vlc_value_t *val )
{
    counter_t *p_counter;

    /* Get stats handler singleton */
170 171 172 173 174 175
    stats_handler_t *p_handler;
    if( p_this->p_libvlc->b_stats == VLC_FALSE )
    {
        return VLC_EGENERIC;
    }
    p_handler = stats_HandlerGet( p_this );
Clément Stenac's avatar
Clément Stenac committed
176 177 178 179
    if( !p_handler ) return VLC_ENOMEM;
    vlc_mutex_lock( &p_handler->object_lock );

    /* Look for existing element */
180 181
    p_counter = GetCounter( p_handler, i_object_id,
                            psz_name );
Clément Stenac's avatar
Clément Stenac committed
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;
Clément Stenac's avatar
Clément Stenac committed
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;
Clément Stenac's avatar
Clément Stenac committed
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;
    }
Clément Stenac's avatar
Clément Stenac committed
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 244 245 246 247
/** 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,
                             char *psz_name )
{
    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,
260 261 262 263 264 265 266 267
                            psz_name );
    vlc_mutex_unlock( &p_handler->object_lock );
    vlc_object_release( p_handler );

    return p_counter;
}


Clément Stenac's avatar
Clément Stenac committed
268 269 270
void stats_ComputeInputStats( input_thread_t *p_input,
                              input_stats_t *p_stats )
{
271 272 273
    vlc_object_t *p_obj;
    vlc_list_t *p_list;
    int i_index;
Clément Stenac's avatar
Clément Stenac committed
274
    vlc_mutex_lock( &p_stats->lock );
275 276

    /* Input */
Clément Stenac's avatar
Clément Stenac committed
277 278 279 280
    stats_GetInteger( p_input, p_input->i_object_id, "read_packets",
                       &p_stats->i_read_packets );
    stats_GetInteger( p_input, p_input->i_object_id, "read_bytes",
                       &p_stats->i_read_bytes );
281
    stats_GetFloat( p_input, p_input->i_object_id, "input_bitrate",
282 283 284 285 286 287 288 289 290 291 292 293
                       &p_stats->f_input_bitrate );

    stats_GetInteger( p_input, p_input->i_object_id, "demux_read",
                      &p_stats->i_demux_read_bytes );
    stats_GetFloat( p_input, p_input->i_object_id, "demux_bitrate",
                      &p_stats->f_demux_bitrate );

    stats_GetInteger( p_input, p_input->i_object_id, "decoded_video",
                      &p_stats->i_decoded_video );
    stats_GetInteger( p_input, p_input->i_object_id, "decoded_audio",
                      &p_stats->i_decoded_audio );

294 295 296 297 298 299 300 301
    /* Sout */
    stats_GetInteger( p_input, p_input->i_object_id, "sout_sent_packets",
                      &p_stats->i_sent_packets );
    stats_GetInteger( p_input, p_input->i_object_id, "sout_sent_bytes",
                      &p_stats->i_sent_bytes );
    stats_GetFloat  ( p_input, p_input->i_object_id, "sout_send_bitrate",
                      &p_stats->f_send_bitrate );

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

308
    /* Vouts - FIXME: Store all in input */
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
    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;
            stats_GetInteger( p_obj, p_obj->i_object_id, "displayed_pictures",
                              &i_displayed );
            stats_GetInteger( p_obj, p_obj->i_object_id, "lost_pictures",
                              &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

Clément Stenac's avatar
Clément Stenac committed
328 329 330 331 332 333
    vlc_mutex_unlock( &p_stats->lock );
}

void stats_ReinitInputStats( input_stats_t *p_stats )
{
    p_stats->i_read_packets = p_stats->i_read_bytes =
334 335 336 337
    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
338
    p_stats->i_played_abuffers = p_stats->i_lost_abuffers =
339 340 341
    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;
Clément Stenac's avatar
Clément Stenac committed
342 343 344 345 346
}

void stats_DumpInputStats( input_stats_t *p_stats  )
{
    vlc_mutex_lock( &p_stats->lock );
347 348
    /* f_bitrate is in bytes / microsecond
     * *1000 => bytes / millisecond => kbytes / seconds */
Clément Stenac's avatar
Clément Stenac committed
349
    fprintf( stderr, "Input : %i (%i bytes) - %f kB/s - Demux : %i (%i bytes) - %f kB/s\n"
350
                     " - Vout : %i/%i - Aout : %i/%i - Vout : %f\n",
351
                    p_stats->i_read_packets, p_stats->i_read_bytes,
352
                    p_stats->f_input_bitrate * 1000,
353 354
                    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
355
                    p_stats->i_displayed_pictures, p_stats->i_lost_pictures,
356 357
                    p_stats->i_played_abuffers, p_stats->i_lost_abuffers,
                    p_stats->f_send_bitrate );
Clément Stenac's avatar
Clément Stenac committed
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
    vlc_mutex_unlock( &p_stats->lock );
}


/********************************************************************
 * 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
 */
375 376 377 378 379 380
static int stats_CounterUpdate( stats_handler_t *p_handler,
                                counter_t *p_counter,
                                vlc_value_t val )
{
    switch( p_counter->i_compute_type )
    {
381 382
    case STATS_LAST:
    case STATS_MIN:
Clément Stenac's avatar
Clément Stenac committed
383
    case STATS_MAX:
384 385 386 387 388
        if( p_counter->i_samples > 1)
        {
            msg_Err( p_handler, "LAST counter has several samples !" );
            return VLC_EGENERIC;
        }
389 390 391 392 393 394 395 396
        if( p_counter->i_type != VLC_VAR_FLOAT &&
            p_counter->i_type != VLC_VAR_INTEGER &&
            p_counter->i_compute_type != STATS_LAST )
        {
            msg_Err( p_handler, "Unable to compute MIN or MAX for this type");
            return VLC_EGENERIC;
        }

397 398 399 400 401 402 403 404 405 406 407
        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 )
        {
408 409 410 411 412 413 414 415 416 417 418 419 420 421
            /* 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 )
                   ) ) )
422
            {
423 424 425 426 427 428
                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;
429 430 431
            }
        }
        break;
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
    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;
    }
461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
    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:
            case VLC_VAR_FLOAT:
                p_counter->pp_samples[0]->value.i_int += val.i_int;
                break;
            default:
                msg_Err( p_handler, "Trying to increment invalid variable %s",
                         p_counter->psz_name );
                return VLC_EGENERIC;
            }
        }
        break;
    }
    return VLC_SUCCESS;
}

495 496
static counter_t *GetCounter( stats_handler_t *p_handler, int i_object_id,
                             char *psz_name )
497 498
{
    int i;
Clément Stenac's avatar
Clément Stenac committed
499
   for( i = 0; i< p_handler->i_counters; i++ )
500 501 502 503 504 505 506 507 508 509 510
    {
        counter_t *p_counter = p_handler->pp_counters[i];
        if( p_counter->i_source_object == i_object_id &&
            !strcmp( p_counter->psz_name, psz_name ) )
        {
            return p_counter;
        }
    }
    return NULL;
}

511

512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556
static stats_handler_t *stats_HandlerGet( vlc_object_t *p_this )
{
    stats_handler_t *p_handler = (stats_handler_t*)
                          vlc_object_find( p_this->p_vlc, VLC_OBJECT_STATS,
                                           FIND_ANYWHERE );
    if( !p_handler )
    {
        p_handler = stats_HandlerCreate( p_this );
        if( !p_handler )
        {
            return NULL;
        }
        vlc_object_yield( p_handler );
    }
    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;
    p_handler->pp_counters = NULL;

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

    return p_handler;
}