[vlc-devel] [PATCH] TS demux: get time and length

Laurent Aimar fenrir at elivagar.org
Tue Sep 20 22:41:44 CEST 2011


On Wed, Sep 21, 2011 at 02:41:04AM +0900, Naohiro KORIYAMA wrote:
> I add a option "Seek based on percent not time", like Matroska demuxer.
> This option's default value is off. If it turns on, force seeking
> based on a percent byte positon.

 One way would be to check the PCR at multiple positions (say something
like 5 or 10) and to force percent byte position seeks when they don't
behave correctly (you can modify your GetLastPCR() to take a percent
position and return a pcr).

> @@ -1196,64 +1174,80 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
>      {
>      case DEMUX_GET_POSITION:
>          pf = (double*) va_arg( args, double* );
> -        i64 = stream_Size( p_demux->s );
> -        if( i64 > 0 )
> -        {
> -            double f_current = stream_Tell( p_demux->s );
> -            *pf = f_current / (double)i64;
> -        }
> -        else
> +
> +        if( (p_sys->b_dvb_meta && p_sys->b_access_control) ||
> +            p_sys->i_current_pcr - p_sys->i_first_pcr < 0 ||
> +            p_sys->i_last_pcr - p_sys->i_first_pcr <= 0 )
>          {
>              int64_t i_time, i_length;
>              if( !DVBEventInformation( p_demux, &i_time, &i_length ) && i_length > 0 )
>                  *pf = (double)i_time/(double)i_length;
> +            else if( (i64 = stream_Size( p_demux->s) ) > 0 )
> +                *pf = (double)stream_Tell( p_demux->s ) / (double)i64;
>              else
>                  *pf = 0.0;
>          }
> +        else
> +        {
> +            int64_t i_time, i_length;
> +            i_time = (p_sys->i_current_pcr - p_sys->i_first_pcr) * 100 / 9;
> +            i_length = (p_sys->i_last_pcr - p_sys->i_first_pcr) * 100 / 9;
> +            *pf = (double)i_time/(double)i_length;
> +        }
 You don't really need the 100/9 factor as they cancel out.

> +static mtime_t FixPCRWrapAround( mtime_t i_pcr )
> +{
> +    /*
> +     * PCR is 33bit. If PCR reaches to 0x1FFFFFFFF, resset from 0.
> +     * So, need to add 0x1FFFFFFFF, when calculation duration or current position.
> +     */
> +    return i_pcr + 0x1FFFFFFFF; /* 33bit */
> +}
I think you could merge the check against i_first_pcr into it (to simplify
the code).

> +static int SeekToPCR( demux_t *p_demux, int64_t i_pos )
> +{
> +    demux_sys_t *p_sys = p_demux->p_sys;
> +
> +    mtime_t i_pcr = -1;
> +
> +    int64_t i_initial_pos = stream_Tell( p_demux->s );
> +
> +    if( i_pos < 0 )
> +        return VLC_EGENERIC;
> +
> +    int64_t i_last_pos = i_pos + p_sys->i_packet_size * 4500;
> +    if( i_last_pos > stream_Size( p_demux->s ) - p_sys->i_packet_size )
> +    {
> +        i_last_pos = stream_Size( p_demux->s ) - p_sys->i_packet_size;
> +    }
> +
> +    if( stream_Seek( p_demux->s, i_pos ) )
> +        return VLC_EGENERIC;
> +
> +    while( vlc_object_alive( p_demux ) )
> +    {
> +        block_t     *p_pkt;
> +        if( !(p_pkt = ReadTSPacket( p_demux )) )
> +        {
> +            break;
> +        }
> +
> +        if( p_sys->i_pmt_es > 0 )
> +        {
> +            if( PIDGet( p_pkt ) == p_sys->i_pid_first_pcr )
> +            {
> +                i_pcr = GetPCR( p_pkt );
> +                if( i_pcr >= 0 )
> +                {
> +                    if( p_sys->i_first_pcr > i_pcr )
> +                    {
> +                        i_pcr = FixPCRWrapAround( i_pcr );
> +                    }
> +                }
> +            }
> +        }
> +        block_Release( p_pkt );
> +
> +        if( i_pcr >= 0 )
> +            break;
> +
> +        if( stream_Tell( p_demux->s ) >= i_last_pos )
> +            break;
> +    }
> +    if( i_pcr < 0 )
> +    {
> +        stream_Seek( p_demux->s, i_initial_pos );
> +        return VLC_EGENERIC;
> +    }
> +    else
> +    {
> +        p_sys->i_current_pcr = i_pcr;
> +        return VLC_SUCCESS;
> +    }
> +}

That could probably be merged into GetLastPCR() (well it might need
another name).

> +static int Seek( demux_t *p_demux, double f_percent )
> +{
> +    demux_sys_t *p_sys = p_demux->p_sys;
> +
> +    int64_t i_initial_pos = stream_Tell( p_demux->s );
> +
> +    /*
> +     * Seek i_msec from current position.
> +     * If seeking to over/under range, seek to first, or seek to last 1sec.
> +     * If cannot seek, return VLC_EGENERIC. 
> +     */
> +    mtime_t i_target_pcr = (p_sys->i_last_pcr - p_sys->i_first_pcr) * f_percent + p_sys->i_first_pcr;
> +    int64_t i_msec = (i_target_pcr - p_sys->i_current_pcr) * 100 / 9 / 1000;
> +    int64_t i_rate = stream_Size( p_demux->s ) * INT64_C(1000000) / ((p_sys->i_last_pcr - p_sys->i_first_pcr) * 100 / 9);
> +    int64_t i_size = stream_Size( p_demux->s );
> +
> +    /*
> +     * Seek by estimated rate until i_msec becomes between -5000 and 5000.
> +     * If it does not converge to within 5 loops, treated as a failure.
> +     */
> +    for( int i = 0; i < 5 && (i_msec < -5000 || 5000 < i_msec); i++ )
> +    {
> +        int64_t i_pos = stream_Tell( p_demux->s );
> +
> +        if( i_msec < 0 && i_pos <= i_rate ||
> +            i_msec > 0 && i_pos >= i_size - i_rate )
> +            return VLC_SUCCESS;
> +
> +        int64_t i_approximate_pos = i_pos + i_rate * i_msec / 1000;
> +        if( i_approximate_pos > i_size - i_rate  )
> +            i_approximate_pos = i_size - i_rate ;
> +        if( i_approximate_pos < 0 )
> +            i_approximate_pos = 0;
> +
> +        mtime_t i_prev_pcr = p_sys->i_current_pcr;
> +        if( SeekToPCR( p_demux, i_approximate_pos ) )
> +            break;
> +
> +        /*
> +         * Adjusting i_msec.
> +         */
> +        if( p_sys->i_current_pcr - i_prev_pcr < 0 )
> +        {
> +            i_msec += (i_prev_pcr - p_sys->i_current_pcr) * 100 / 9 / 1000;
> +        }
> +        else
> +        {
> +            i_msec -= (p_sys->i_current_pcr - i_prev_pcr) * 100 / 9 / 1000;
> +        }
> +    }
> +    if( i_msec < -5000 || 5000 < i_msec )
> +    {
> +        msg_Dbg( p_demux, "cannot find a seek position #1" );
> +        stream_Seek( p_demux->s, i_initial_pos );
> +        return VLC_EGENERIC;
> +    }
> +
> +    if( i_msec < 0 )
> +    {
> +        /*
> +         * Seek before per about a second.
> +         * If it does not converge to within 10 loops, treated as a failure.
> +         */
> +        for( int i = 0; i < 10 && i_msec < -500; i++ )
> +        {
> +            int64_t i_pos = stream_Tell( p_demux->s );
> +            if(i_pos <= i_rate )
> +                return VLC_SUCCESS;
> +
> +            mtime_t i_prev_pcr = p_sys->i_current_pcr;
> +            if( SeekToPCR( p_demux, i_pos - i_rate ) )
> +                break;
> +            if( i_prev_pcr - p_sys->i_current_pcr < 0 )
> +                break;
> +            i_msec += (i_prev_pcr - p_sys->i_current_pcr) * 100 / 9 / 1000;
> +        }
> +        if( i_msec < -500 )
> +        {
> +            msg_Dbg( p_demux, "cannot find a seek position #2" );
> +            stream_Seek( p_demux->s, i_initial_pos );
> +            return VLC_EGENERIC;
> +        }
> +    }
> +    else
> +    {
> +        /*
> +         * Seek after per about a second.
> +         * If it does not converge to within 10 loops, treated as a failure.
> +         */
> +        for( int i = 0; i < 10 && i_msec > 500; i++ )
> +        {
> +            int64_t i_pos = stream_Tell( p_demux->s );
> +            if( i_pos >= i_size - i_rate  )
> +                return VLC_SUCCESS;
> +
> +            mtime_t i_prev_pcr = p_sys->i_current_pcr;
> +            if( SeekToPCR( p_demux, i_pos + i_rate ) )
> +                break;
> +            if( p_sys->i_current_pcr - i_prev_pcr < 0 )
> +                break;
> +            i_msec -= (p_sys->i_current_pcr - i_prev_pcr) * 100 / 9 / 1000;
> +        }
> +        if( i_msec > 500 )
> +        {
> +            msg_Dbg( p_demux, "cannot find a seek position #3" );
> +            stream_Seek( p_demux->s, i_initial_pos );
> +            return VLC_EGENERIC;
> +        }
> +    }
> +    return VLC_SUCCESS;
> +}
 I think you could simplify it by using a binary search algorithm.

> +static void GetFirstPCR( demux_t *p_demux )
> +{
> +    demux_sys_t *p_sys = p_demux->p_sys;
> +
> +    p_sys->i_first_pcr = -1;
> +
> +    int64_t i_initial_pos = stream_Tell( p_demux->s );
> +
> +    while( vlc_object_alive (p_demux) )
> +    {
> +        block_t     *p_pkt;
> +        if( !(p_pkt = ReadTSPacket( p_demux )) )
> +        {
> +            break;
> +        }
> +
> +        /* Parse the TS packet */
> +        ts_pid_t *p_pid = &p_sys->pid[PIDGet( p_pkt )];
> +
> +        if( p_pid->b_valid )
> +        {
> +            if( p_pid->psi )
> +            {
> +                if( p_pid->i_pid == 0 || ( p_sys->b_dvb_meta && ( p_pid->i_pid == 0x11 || p_pid->i_pid == 0x12 || p_pid->i_pid == 0x14 ) ) )
> +                {
> +                    dvbpsi_PushPacket( p_pid->psi->handle, p_pkt->p_buffer );
> +                }
> +                else
> +                {
> +                    for( int i_prg = 0; i_prg < p_pid->psi->i_prg; i_prg++ )
> +                    {
> +                        dvbpsi_PushPacket( p_pid->psi->prg[i_prg]->handle,
> +                                           p_pkt->p_buffer );
> +                    }
> +                }
> +            }
> +        }
> +        if( p_sys->i_pmt_es > 0 )
> +        {
> +            mtime_t i_pcr = GetPCR( p_pkt );
> +            if ( i_pcr >= 0 )
> +            {
> +               p_sys->i_pid_first_pcr = p_pid->i_pid;
> +               p_sys->i_first_pcr = i_pcr;
> +            }
> +        }
> +        block_Release( p_pkt );
> +        
> +        if( p_sys->i_first_pcr >= 0 )
> +            break;
> +    }

 I don't think you need all that. A simple (pseudo code):
while( vlc_object_alive (p_demux) )
{
 p_pkt = ReadTSPacket();
 mtime_t i_pcr = GetPCR( p_pkt );
 if( i_pcr >= 0 )
 {
   
  ...
  break;
 } 
 block_Release();
}
should be enough.

> +static void GetLastPCR( demux_t *p_demux )
> +{
> +    demux_sys_t *p_sys = p_demux->p_sys;
> +
> +    p_sys->i_last_pcr = -1;
> +
> +    int64_t i_initial_pos = stream_Tell( p_demux->s );
> +
> +    int64_t i_last_pos = stream_Size( p_demux->s ) - p_sys->i_packet_size;
> +    int64_t i_pos = i_last_pos - p_sys->i_packet_size * 4500; /* FIXME if the value (4500) is not reasonable, please change it. */
> +    if( i_pos < 0 )
> +        return;
> +
> +    if( stream_Seek( p_demux->s, i_pos ) )
> +        return;
> +
> +    while( vlc_object_alive( p_demux ) )
> +    {
> +        block_t     *p_pkt;
> +        if( !(p_pkt = ReadTSPacket( p_demux )) )
> +        {
> +            break;
> +        }
> +
> +        if( p_sys->i_pmt_es > 0 )
> +        {
 I don't think you need to check for i_pmt_es.

> +            if( PIDGet( p_pkt ) == p_sys->i_pid_first_pcr )
> +            {
> +                mtime_t i_pcr = GetPCR( p_pkt );
> +                if( i_pcr >= 0 )
> +                {
> +                    if( p_sys->i_first_pcr > i_pcr )
> +                    {
> +                        p_sys->i_last_pcr = FixPCRWrapAround( i_pcr );
> +                    }
> +                    else
> +                    {
> +                        p_sys->i_last_pcr = i_pcr;
> +                    }
> +                }
> +            }
> +        }
> +        block_Release( p_pkt );
> +        if( stream_Tell( p_demux->s ) >= i_last_pos )
> +            break;
> +    }
> +
> +    stream_Seek( p_demux->s, i_initial_pos );
> +}
> +

Regards,

-- 
fenrir




More information about the vlc-devel mailing list