]> git.sesse.net Git - vlc/blobdiff - modules/demux/ts.c
MKV: the Block duration is for the whole Block, not each frame
[vlc] / modules / demux / ts.c
index 1324a8034de941264dd0503f4d97ed176aeb62bc..4603f2d9b97436a4382439491f0653901f382c0b 100644 (file)
@@ -453,6 +453,7 @@ static block_t* ReadTSPacket( demux_t *p_demux );
 static int ProbeStart( demux_t *p_demux, int i_program );
 static int ProbeEnd( demux_t *p_demux, int i_program );
 static int SeekToTime( demux_t *p_demux, ts_prg_psi_t *, int64_t time );
+static void ReadyQueuesPostSeek( demux_sys_t *p_sys );
 static void PCRHandle( demux_t *p_demux, ts_pid_t *, block_t * );
 static void PCRFixHandle( demux_t *, ts_prg_psi_t *, block_t * );
 static int64_t TimeStampWrapAround( ts_prg_psi_t *, int64_t );
@@ -1525,6 +1526,7 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
             if( !DVBEventInformation( p_demux, &i_time, &i_length ) &&
                  i_length > 0 && !SeekToTime( p_demux, p_prg, TO_SCALE(i_length) * f ) )
             {
+                ReadyQueuesPostSeek( p_sys );
                 es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME,
                                 TO_SCALE(i_length) * f );
                 return VLC_SUCCESS;
@@ -1540,6 +1542,7 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
                                                    p_prg->i_last_dts ) - p_prg->pcr.i_first;
             if( !SeekToTime( p_demux, p_prg, p_prg->pcr.i_first + i_length * f ) )
             {
+                ReadyQueuesPostSeek( p_sys );
                 es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME,
                                 FROM_SCALE(p_prg->pcr.i_first + i_length * f) );
                 return VLC_SUCCESS;
@@ -1550,6 +1553,22 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
         if( i64 > 0 &&
             stream_Seek( p_sys->stream, (int64_t)(i64 * f) ) == VLC_SUCCESS )
         {
+            ReadyQueuesPostSeek( p_sys );
+            return VLC_SUCCESS;
+        }
+        break;
+
+    case DEMUX_SET_TIME:
+        i64 = (int64_t)va_arg( args, int64_t );
+
+        if( p_sys->b_canseek &&
+           (p_prg = GetProgramByID( p_sys, i_first_program )) &&
+            p_prg->pcr.i_first > -1 &&
+           !SeekToTime( p_demux, p_prg, p_prg->pcr.i_first + TO_SCALE(i64) ) )
+        {
+            ReadyQueuesPostSeek( p_sys );
+            es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME,
+                            FROM_SCALE(p_prg->pcr.i_first) + i64 - VLC_TS_0 );
             return VLC_SUCCESS;
         }
         break;
@@ -2124,11 +2143,14 @@ static block_t *Opus_Parse(demux_t *demux, block_t *block)
 /****************************************************************************
  * gathering stuff
  ****************************************************************************/
-static int ParsePESHeader( demux_t *p_demux, const uint8_t *p_header,
+static int ParsePESHeader( demux_t *p_demux, const uint8_t *p_header, size_t i_header,
                            unsigned *pi_skip, mtime_t *pi_dts, mtime_t *pi_pts )
 {
     unsigned i_skip;
 
+    if ( i_header < 9 )
+        return VLC_EGENERIC;
+
     switch( p_header[3] )
     {
     case 0xBC:  /* Program stream map */
@@ -2146,21 +2168,33 @@ static int ParsePESHeader( demux_t *p_demux, const uint8_t *p_header,
         {
             /* mpeg2 PES */
             i_skip = p_header[8] + 9;
+            if( i_header < i_skip )
+                return VLC_EGENERIC;
 
             if( p_header[7]&0x80 )    /* has pts */
             {
+                if( i_header < 9 + 5 )
+                    return VLC_EGENERIC;
                 *pi_pts = ExtractPESTimestamp( &p_header[9] );
 
                 if( p_header[7]&0x40 )    /* has dts */
+                {
+                    if( i_header < 14 + 5 )
+                        return VLC_EGENERIC;
                     *pi_dts = ExtractPESTimestamp( &p_header[14] );
+                }
             }
         }
         else
         {
             i_skip = 6;
+            if( i_header < i_skip + 1 )
+                return VLC_EGENERIC;
             while( i_skip < 23 && p_header[i_skip] == 0xff )
             {
                 i_skip++;
+                if( i_header < i_skip + 1 )
+                    return VLC_EGENERIC;
             }
             if( i_skip == 23 )
             {
@@ -2172,12 +2206,19 @@ static int ParsePESHeader( demux_t *p_demux, const uint8_t *p_header,
                 i_skip += 2;
             }
 
+            if( i_header < i_skip + 1 )
+                return VLC_EGENERIC;
+
             if(  p_header[i_skip]&0x20 )
             {
+                if( i_header < i_skip + 5 )
+                    return VLC_EGENERIC;
                 *pi_pts = ExtractPESTimestamp( &p_header[i_skip] );
 
                 if( p_header[i_skip]&0x10 )    /* has dts */
                 {
+                    if( i_header < i_skip + 10 )
+                        return VLC_EGENERIC;
                     *pi_dts = ExtractPESTimestamp( &p_header[i_skip+5] );
                     i_skip += 10;
                 }
@@ -2194,6 +2235,9 @@ static int ParsePESHeader( demux_t *p_demux, const uint8_t *p_header,
         break;
     }
 
+    if( i_header < i_skip )
+        return VLC_EGENERIC;
+
     *pi_skip = i_skip;
     return VLC_SUCCESS;
 }
@@ -2208,10 +2252,8 @@ static void ParsePES( demux_t *p_demux, ts_pid_t *pid, block_t *p_pes )
     mtime_t i_pts = -1;
     mtime_t i_length = 0;
 
-    /* FIXME find real max size */
     const int i_max = block_ChainExtract( p_pes, header, 34 );
-    assert(i_max >= 34);
-    if (unlikely(i_max < 34))
+    if ( i_max < 4 )
     {
         block_ChainRelease( p_pes );
         return;
@@ -2226,8 +2268,7 @@ static void ParsePES( demux_t *p_demux, ts_pid_t *pid, block_t *p_pes )
         return;
     }
 
-    /* TODO check size */
-    if( ParsePESHeader( p_demux, (uint8_t*)&header, &i_skip, &i_dts, &i_pts ) == VLC_EGENERIC )
+    if( ParsePESHeader( p_demux, (uint8_t*)&header, i_max, &i_skip, &i_dts, &i_pts ) == VLC_EGENERIC )
     {
         block_ChainRelease( p_pes );
         return;
@@ -2604,6 +2645,45 @@ static mtime_t GetPCR( block_t *p_pkt )
     return i_pcr;
 }
 
+static inline void FlushESBuffer( ts_es_t *p_es )
+{
+    if( p_es->p_data )
+    {
+        p_es->i_data_gathered = p_es->i_data_size = 0;
+        block_ChainRelease( p_es->p_data );
+        p_es->p_data = NULL;
+        p_es->pp_last = &p_es->p_data;
+    }
+}
+
+static void ReadyQueuesPostSeek( demux_sys_t *p_sys )
+{
+    for( int i=MIN_ES_PID; i<=MAX_ES_PID; i++ )
+    {
+        ts_pid_t *pid = &p_sys->pid[i];
+
+        if( !pid->es )
+            continue;
+
+        pid->i_cc = 0xff;
+
+        if( pid->es->p_prepcr_outqueue )
+        {
+            block_ChainRelease( pid->es->p_prepcr_outqueue );
+            pid->es->p_prepcr_outqueue = NULL;
+        }
+
+        FlushESBuffer( pid->es );
+
+        for( int j = 0; j < pid->i_extra_es; j++ )
+            FlushESBuffer( pid->extra_es[j] );
+    }
+
+    for( int i=0; i<p_sys->i_pmt; i++ )
+        for( int j=0; j<p_sys->pmt[i]->psi->i_prg; j++ )
+            p_sys->pmt[i]->psi->prg[j]->pcr.i_current = -1;
+}
+
 static int SeekToTime( demux_t *p_demux, ts_prg_psi_t *p_prg, int64_t i_scaledtime )
 {
     demux_sys_t *p_sys = p_demux->p_sys;
@@ -2657,14 +2737,18 @@ static int SeekToTime( demux_t *p_demux, ts_prg_psi_t *p_prg, int64_t i_scaledti
                 unsigned i_skip = 4;
                 if ( p_pkt->p_buffer[3] & 0x20 ) // adaptation field
                 {
-                    i_pcr = GetPCR( p_pkt );
-                    i_skip += 1 + p_pkt->p_buffer[4];
+                    if( p_pkt->i_buffer >= 4 + 2 + 5 )
+                    {
+                        i_pcr = GetPCR( p_pkt );
+                        i_skip += 1 + p_pkt->p_buffer[4];
+                    }
                 }
                 else
                 {
                     mtime_t i_dts = -1;
                     mtime_t i_pts = -1;
-                    if ( VLC_SUCCESS == ParsePESHeader( p_demux, &p_pkt->p_buffer[i_skip], &i_skip, &i_dts, &i_pts ) )
+                    if ( VLC_SUCCESS == ParsePESHeader( p_demux, &p_pkt->p_buffer[i_skip],
+                                                        p_pkt->i_buffer - i_skip, &i_skip, &i_dts, &i_pts ) )
                     {
                         if( i_dts > -1 )
                             i_pcr = i_dts;
@@ -2728,6 +2812,7 @@ static int ProbeChunk( demux_t *p_demux, int i_program, bool b_end, int64_t *pi_
         }
 
         int i_pid = PIDGet( p_pkt );
+        p_sys->pid[i_pid].b_seen = true;
 
         if( i_pid != 0x1FFF && p_sys->pid[i_pid].b_valid && p_sys->pid[i_pid].p_owner &&
            (p_pkt->p_buffer[1] & 0xC0) == 0x40 && /* Payload start but not corrupt */
@@ -2736,7 +2821,8 @@ static int ProbeChunk( demux_t *p_demux, int i_program, bool b_end, int64_t *pi_
         {
             bool b_pcrresult = true;
 
-            *pi_pcr = GetPCR( p_pkt );
+            if( p_pkt->i_buffer >= 4 + 2 + 5 )
+                *pi_pcr = GetPCR( p_pkt );
 
             if( *pi_pcr == -1 )
             {
@@ -2747,7 +2833,9 @@ static int ProbeChunk( demux_t *p_demux, int i_program, bool b_end, int64_t *pi_
                 if ( p_pkt->p_buffer[3] & 0x20 ) // adaptation field
                     i_skip += 1 + p_pkt->p_buffer[4];
 
-                if ( VLC_SUCCESS == ParsePESHeader( p_demux, &p_pkt->p_buffer[i_skip], &i_skip, &i_dts, &i_pts ) )
+                if ( VLC_SUCCESS == ParsePESHeader( p_demux, &p_pkt->p_buffer[i_skip],
+                                                    p_pkt->i_buffer - i_skip,
+                                                    &i_skip, &i_dts, &i_pts ) )
                 {
                     if( i_dts != -1 )
                         *pi_pcr = i_dts;
@@ -2851,6 +2939,39 @@ static void ProgramSetPCR( demux_t *p_demux, ts_prg_psi_t *p_prg, mtime_t i_pcr
 {
     demux_sys_t *p_sys = p_demux->p_sys;
 
+    /* Check if we have enqueued blocks waiting the/before the
+       PCR barrier, and then adapt pcr so they have valid PCR when dequeuing */
+    if( p_prg->pcr.i_current == -1 )
+    {
+        mtime_t i_mindts = -1;
+        for( int i=MIN_ES_PID; i<=MAX_ES_PID; i++ )
+        {
+            ts_pid_t *p_pid = &p_sys->pid[i];
+            if( p_pid->b_valid && p_pid->i_owner_number == p_prg->i_number && p_pid->es )
+            {
+                block_t *p_block = p_pid->es->p_prepcr_outqueue;
+                while( p_block && p_block->i_dts == VLC_TS_INVALID )
+                    p_block = p_block->p_next;
+
+                if( p_block )
+                    msg_Warn( p_demux, "Program %ld %ld *******", i_mindts, p_block->i_dts );
+
+                if( p_block && ( i_mindts == -1 || p_block->i_dts < i_mindts ) )
+                {
+                    i_mindts = p_block->i_dts;
+                    msg_Warn( p_demux, "Program %ld %ld *******", i_mindts, p_block->i_dts );
+                }
+            }
+        }
+
+        if( i_mindts > VLC_TS_INVALID )
+        {
+            msg_Dbg( p_demux, "Program %d PCR prequeue fixup %"PRId64"->%"PRId64,
+                     p_prg->i_number, TO_SCALE(i_mindts), i_pcr );
+            i_pcr = TO_SCALE(i_mindts);
+        }
+    }
+
     p_prg->pcr.i_current = i_pcr;
     if( p_prg->pcr.i_first == -1 )
     {
@@ -2914,11 +3035,13 @@ static void PCRHandle( demux_t *p_demux, ts_pid_t *pid, block_t *p_bk )
 static int FindPCRCandidate( demux_sys_t *p_sys, ts_prg_psi_t *p_prg )
 {
     ts_pid_t *p_cand = NULL;
+    int i_previous = p_prg->i_pid_pcr;
     for( int i=MIN_ES_PID; i<=MAX_ES_PID; i++ )
     {
         ts_pid_t *p_pid = &p_sys->pid[i];
         if( p_pid->b_seen && p_pid->es && p_pid->es->id &&
-            p_pid->i_owner_number == p_prg->i_number )
+            p_pid->i_owner_number == p_prg->i_number &&
+            p_cand->i_pid != i_previous )
         {
             if( p_pid->probed.i_pcr_count ) /* check PCR frequency first */
             {
@@ -5448,6 +5571,15 @@ static void PMTCallBack( void *data, dvbpsi_pmt_t *p_pmt )
     if( i_clean )
         free( pp_clean );
 
+    if( !p_sys->b_trust_pcr )
+    {
+        int i_cand = FindPCRCandidate( p_demux->p_sys, prg );
+        prg->i_pid_pcr = i_cand;
+        prg->pcr.b_disable = true;
+        msg_Warn( p_demux, "PCR not trusted for program %d, set up workaround using pid %d",
+                  prg->i_number, i_cand );
+    }
+
     /* Probe Boundaries */
     if( p_sys->b_canfastseek && prg->i_last_dts == -1 )
     {