[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