input_clock.c 10.9 KB
Newer Older
1
/*****************************************************************************
2
 * input_clock.c: Clock/System date convertions, stream management
3 4
 *****************************************************************************
 * Copyright (C) 1999, 2000 VideoLAN
Sam Hocevar's avatar
 
Sam Hocevar committed
5
 * $Id: input_clock.c,v 1.10 2001/04/26 03:55:44 sam Exp $
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
 *
 * Authors: Christophe Massiot <massiot@via.ecp.fr>
 *
 * 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
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include "defs.h"

Sam Hocevar's avatar
 
Sam Hocevar committed
29 30
#include <string.h>                                    /* memcpy(), memset() */

31 32 33 34 35 36 37 38 39 40 41 42
#include "config.h"
#include "common.h"
#include "threads.h"
#include "mtime.h"
#include "intf_msg.h"

#include "stream_control.h"
#include "input_ext-intf.h"
#include "input_ext-dec.h"

#include "input.h"

43
/*
44
 * DISCUSSION : SYNCHRONIZATION METHOD
45
 *
46 47 48 49 50 51 52
 * In some cases we can impose the pace of reading (when reading from a
 * file or a pipe), and for the synchronization we simply sleep() until
 * it is time to deliver the packet to the decoders. When reading from
 * the network, we must be read at the same pace as the server writes,
 * otherwise the kernel's buffer will trash packets. The risk is now to
 * overflow the input buffers in case the server goes too fast, that is
 * why we do these calculations :
53
 *
54 55 56 57
 * We compute a mean for the pcr because we want to eliminate the
 * network jitter and keep the low frequency variations. The mean 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.
58
 *
59 60 61 62 63 64 65
 * 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 decode (or trash) 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.
66
 *
67 68
 * It is a very important matter if you want to avoid underflow or overflow
 * in all the FIFOs, but it may be not enough.
69 70
 */

71
/*****************************************************************************
72
 * Constants
73
 *****************************************************************************/
74 75 76

/* Maximum number of samples used to compute the dynamic average value.
 * We use the following formula :
77 78
 * new_average = (old_average * c_average + new_sample_value) / (c_average +1)
 */
79 80 81 82 83 84 85 86 87 88
#define CR_MAX_AVERAGE_COUNTER 40

/* Maximum gap allowed between two CRs. */
#define CR_MAX_GAP 1000000

/*****************************************************************************
 * ClockToSysdate: converts a movie clock to system date
 *****************************************************************************/
static mtime_t ClockToSysdate( input_thread_t * p_input,
                               pgrm_descriptor_t * p_pgrm, mtime_t i_clock )
89 90 91 92 93
{
    mtime_t     i_sysdate = 0;

    if( p_pgrm->i_synchro_state == SYNCHRO_OK )
    {
94 95 96 97 98 99
        i_sysdate = (mtime_t)(i_clock - p_pgrm->cr_ref) 
                        * (mtime_t)p_input->stream.control.i_rate
                        * (mtime_t)300
                        / (mtime_t)27
                        / (mtime_t)DEFAULT_RATE
                        + (mtime_t)p_pgrm->sysdate_ref;
100 101 102 103 104
    }

    return( i_sysdate );
}

105 106 107 108 109 110 111 112 113 114 115 116 117
/*****************************************************************************
 * ClockCurrent: converts current system date to clock units
 *****************************************************************************
 * Caution : the synchro state must be SYNCHRO_OK for this to operate.
 *****************************************************************************/
static mtime_t ClockCurrent( input_thread_t * p_input,
                             pgrm_descriptor_t * p_pgrm )
{
    return( (mdate() - p_pgrm->sysdate_ref) * 27 * DEFAULT_RATE
             / p_input->stream.control.i_rate / 300
             + p_pgrm->cr_ref );
}

118
/*****************************************************************************
119
 * ClockNewRef: writes a new clock reference
120
 *****************************************************************************/
121 122
static void ClockNewRef( input_thread_t * p_input, pgrm_descriptor_t * p_pgrm,
                         mtime_t i_clock, mtime_t i_sysdate )
123 124
{
    p_pgrm->cr_ref = i_clock;
125
    p_pgrm->sysdate_ref = i_sysdate;
126 127
}

128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
/*****************************************************************************
 * input_ClockInit: reinitializes the clock reference after a stream
 *                  discontinuity
 *****************************************************************************/
void input_ClockInit( pgrm_descriptor_t * p_pgrm )
{
    p_pgrm->last_cr = 0;
    p_pgrm->cr_ref = 0;
    p_pgrm->sysdate_ref = 0;
    p_pgrm->delta_cr = 0;
    p_pgrm->c_average_count = 0;
}

/*****************************************************************************
 * input_ClockManageRef: manages a clock reference
 *****************************************************************************/
void input_ClockManageRef( input_thread_t * p_input,
                           pgrm_descriptor_t * p_pgrm, mtime_t i_clock )
{
    if( p_pgrm->i_synchro_state != SYNCHRO_OK )
    {
        /* Feed synchro with a new reference point. */
150
        ClockNewRef( p_input, p_pgrm, i_clock, mdate() );
151 152 153 154 155 156 157 158 159 160 161 162 163 164
        p_pgrm->i_synchro_state = SYNCHRO_OK;
    }
    else
    {
        if ( p_pgrm->last_cr != 0 &&
               (    (p_pgrm->last_cr - i_clock) > CR_MAX_GAP
                 || (p_pgrm->last_cr - i_clock) < - CR_MAX_GAP ) )
        {
            /* Stream discontinuity, for which we haven't received a
             * warning from the stream control facilities (dd-edited
             * stream ?). */
            intf_WarnMsg( 3, "Clock gap, unexpected stream discontinuity" );
            input_ClockInit( p_pgrm );
            p_pgrm->i_synchro_state = SYNCHRO_START;
165
            input_EscapeDiscontinuity( p_input, p_pgrm );
166 167 168 169 170 171 172 173 174 175 176
        }

        p_pgrm->last_cr = i_clock;

        if( p_input->stream.b_pace_control
             && p_input->stream.pp_programs[0] == p_pgrm )
        {
            /* Wait a while before delivering the packets to the decoder.
             * In case of multiple programs, we arbitrarily follow the
             * clock of the first program. */
            mwait( ClockToSysdate( p_input, p_pgrm, i_clock ) );
177 178 179

            /* Now take into account interface changes. */
            vlc_mutex_lock( &p_input->stream.stream_lock );
Sam Hocevar's avatar
 
Sam Hocevar committed
180 181

            if( p_input->stream.i_new_status == PAUSE_S )
182
            {
Sam Hocevar's avatar
 
Sam Hocevar committed
183 184 185 186 187 188 189 190 191
                int i_old_status;
                vlc_mutex_lock( &p_input->stream.control.control_lock );
                i_old_status = p_input->stream.control.i_status;

                p_input->stream.control.i_status = PAUSE_S;
                vlc_cond_wait( &p_input->stream.stream_wait,
                               &p_input->stream.stream_lock );
                ClockNewRef( p_input, p_pgrm, i_clock, mdate() );

192
                if( p_input->stream.i_new_status == PAUSE_S )
Sam Hocevar's avatar
 
Sam Hocevar committed
193 194 195 196 197
                { 
                    /* PAUSE_S undoes the pause state: Return to old state. */
                    p_input->stream.control.i_status = i_old_status;
                    p_input->stream.i_new_status = UNDEF_S;
                    p_input->stream.i_new_rate = UNDEF_S;
198
                }
199

Sam Hocevar's avatar
 
Sam Hocevar committed
200 201 202 203 204 205
                /*  We handle i_new_status != PAUSE_S below... */
                vlc_mutex_unlock( &p_input->stream.control.control_lock );
            }

            if( p_input->stream.i_new_status != UNDEF_S )
            {
206
                vlc_mutex_lock( &p_input->stream.control.control_lock );
Sam Hocevar's avatar
 
Sam Hocevar committed
207

208 209
                p_input->stream.control.i_status = p_input->stream.i_new_status;

Sam Hocevar's avatar
 
Sam Hocevar committed
210 211 212 213 214 215 216 217 218
                ClockNewRef( p_input, p_pgrm, i_clock,
                           ClockToSysdate( p_input, p_pgrm, i_clock ) );

                if( p_input->stream.control.i_status == PLAYING_S )
                {
                    p_input->stream.control.i_rate = DEFAULT_RATE;
                    p_input->stream.control.b_mute = 0;
                }
                else
219 220 221 222 223 224
                {
                    p_input->stream.control.i_rate = p_input->stream.i_new_rate;
                    p_input->stream.control.b_mute = 1;

                    /* Feed the audio decoders with a NULL packet to avoid
                     * discontinuities. */
225
                    input_EscapeAudioDiscontinuity( p_input, p_pgrm );
226 227 228 229
                }

                p_input->stream.i_new_status = UNDEF_S;
                p_input->stream.i_new_rate = UNDEF_S;
Sam Hocevar's avatar
 
Sam Hocevar committed
230 231

                vlc_mutex_unlock( &p_input->stream.control.control_lock );
232
            }
Sam Hocevar's avatar
 
Sam Hocevar committed
233

234
            vlc_mutex_unlock( &p_input->stream.stream_lock );
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
        }
        else
        {
            /* Smooth clock reference variations. */
            mtime_t     i_extrapoled_clock = ClockCurrent( p_input, p_pgrm );

            /* Bresenham algorithm to smooth variations. */
            if( p_pgrm->c_average_count == CR_MAX_AVERAGE_COUNTER )
            {
                p_pgrm->delta_cr = ( p_pgrm->delta_cr
                                        * (CR_MAX_AVERAGE_COUNTER - 1)
                                      + i_extrapoled_clock )
                                    / CR_MAX_AVERAGE_COUNTER;
            }
            else
            {
                p_pgrm->delta_cr = ( p_pgrm->delta_cr
                                        * p_pgrm->c_average_count
                                      + i_extrapoled_clock )
                                    / (p_pgrm->c_average_count + 1);
                p_pgrm->c_average_count++;
            }
        }
    }
}

/*****************************************************************************
 * input_ClockGetTS: manages a PTS or DTS
 *****************************************************************************/
mtime_t input_ClockGetTS( input_thread_t * p_input,
                          pgrm_descriptor_t * p_pgrm, mtime_t i_ts )
{
    if( p_pgrm->i_synchro_state == SYNCHRO_OK )
    {
        return( ClockToSysdate( p_input, p_pgrm, i_ts + p_pgrm->delta_cr )
                 + DEFAULT_PTS_DELAY );
    }
    else
    {
        return 0;
    }
}