/*******************************************************************************
 * pcr.c: PCR management 
 * (c)1999 VideoLAN
 *******************************************************************************
 * Manages structures containing PCR information.
 *******************************************************************************/

/*******************************************************************************
 * Preamble
 *******************************************************************************/
#include <stdio.h>
#include <sys/uio.h>                                                 /* iovec */
#include <stdlib.h>                               /* atoi(), malloc(), free() */
#include <netinet/in.h>

#include "config.h"
#include "common.h"
#include "mtime.h"
#include "vlc_thread.h"
#include "debug.h"
#include "input.h"
#include "intf_msg.h"
#include "input_pcr.h"

/* Note:
 *   
 *   SYNCHRONIZATION METHOD
 *   
 *   We compute an average for the pcr because we want to eliminate the
 *   network jitter and keep the low frequency variations. The average is
 *   in fact a low pass filter and the jitter is a high frequency signal
 *   that is why it is eliminated by the filter/average.
 *   
 *   The low frequency variations enable us to synchronize the client clock
 *   with the server clock because they represent the time variation between
 *   the 2 clocks. Those variations (ie the filtered pcr) are used to compute
 *   the presentation dates for the audio and video frames. With those dates
 *   we can decoding (or trashing) the MPEG2 stream at "exactly" the same rate
 *   as it is sent by the server and so we keep the synchronization between
 *   the server and the client.
 *   
 *   It is a very important matter if you want to avoid underflow or overflow
 *   in all the FIFOs, but it may be not enough.
 *
 */

/******************************************************************************
 * input_PcrReInit : Reinitialize the pcr_descriptor
 ******************************************************************************/
void input_PcrReInit( input_thread_t *p_input )
{
    ASSERT( p_input );

    p_input->p_pcr->delta_pcr       = 0;
    p_input->p_pcr->last_pcr        = 0;
    p_input->p_pcr->c_average_count = 0;
}

/******************************************************************************
 * input_PcrInit : Initialize PCR decoder
 ******************************************************************************/
int input_PcrInit( input_thread_t *p_input )
{
    ASSERT( p_input );

    if( (p_input->p_pcr = malloc(sizeof(pcr_descriptor_t))) == NULL )
    {
        return( -1 );
    }
    input_PcrReInit(p_input);
    p_input->p_pcr->i_synchro_state = SYNCHRO_NOT_STARTED;
    
    return( 0 );
}

/******************************************************************************
 * input_PcrDecode : Decode a PCR frame
 ******************************************************************************/
void input_PcrDecode( input_thread_t *p_input, es_descriptor_t *p_es,
                      u8* p_pcr_data )
{
    mtime_t pcr_time, sys_time, delta_pcr;
    pcr_descriptor_t *p_pcr;
        
    ASSERT( p_pcr_data );
    ASSERT( p_input );
    ASSERT( p_es );

    p_pcr = p_input->p_pcr;
    
    /* Convert the PCR in microseconde
     * WARNING: do not remove the casts in the following calculation ! */
    pcr_time  = ( (( (mtime_t)U32_AT((u32*)p_pcr_data) << 1 ) | ( p_pcr_data[4] >> 7 )) * 300 ) / 27;
    sys_time  = mdate();
    delta_pcr = sys_time - pcr_time;
    
    if( p_es->b_discontinuity ||
        ( p_pcr->last_pcr != 0 && 
	      (    (p_pcr->last_pcr - pcr_time) > PCR_MAX_GAP
	        || (p_pcr->last_pcr - pcr_time) < - PCR_MAX_GAP ) ) )
    {
        intf_DbgMsg("input debug: input_PcrReInit()\n");
        input_PcrReInit(p_input);
        p_pcr->i_synchro_state = SYNCHRO_REINIT;
        p_es->b_discontinuity = 0;
    }
    p_pcr->last_pcr = pcr_time;

    if( p_pcr->c_average_count == PCR_MAX_AVERAGE_COUNTER )
    {
        p_pcr->delta_pcr = 
            ( delta_pcr + (p_pcr->delta_pcr * (PCR_MAX_AVERAGE_COUNTER-1)) )
            / PCR_MAX_AVERAGE_COUNTER;
    }
    else
    {
        p_pcr->delta_pcr =
            ( delta_pcr + (p_pcr->delta_pcr * p_pcr->c_average_count) )
            / ( p_pcr->c_average_count + 1 );
        p_pcr->c_average_count++;
    }

    if( p_pcr->i_synchro_state == SYNCHRO_NOT_STARTED )
    {
        p_pcr->i_synchro_state = SYNCHRO_START;
    }
}

/******************************************************************************
 * input_PcrClean : Clean PCR structures before dying
 ******************************************************************************/
void input_PcrClean( input_thread_t *p_input )
{
    ASSERT( p_input );

    free( p_input->p_pcr );
}
