[vlc-commits] demux: ts: fix pusi flag conformance

Francois Cartegnie git at videolan.org
Wed Nov 9 17:22:12 CET 2016


vlc | branch: master | Francois Cartegnie <fcvlcdev at free.fr> | Tue Nov  8 20:08:30 2016 +0100| [eb3bb856069d75acd0d89d1662a4617f8b8c5ebb] | committer: Francois Cartegnie

demux: ts: fix pusi flag conformance

Non payload start unit flagged packets can contain
multiple PES payloads.

Also adds workaround for broken, non pusi compliant
streams as seen on Adtech's 0x06 type.

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=eb3bb856069d75acd0d89d1662a4617f8b8c5ebb
---

 modules/demux/mpeg/ts.c                 | 285 +++++++++++++++++++++++++-------
 modules/demux/mpeg/ts_streams.c         |  13 +-
 modules/demux/mpeg/ts_streams_private.h |  16 +-
 3 files changed, 242 insertions(+), 72 deletions(-)

diff --git a/modules/demux/mpeg/ts.c b/modules/demux/mpeg/ts.c
index 7f3fbac..52a0105 100644
--- a/modules/demux/mpeg/ts.c
+++ b/modules/demux/mpeg/ts.c
@@ -1225,7 +1225,7 @@ invalid:
 /****************************************************************************
  * gathering stuff
  ****************************************************************************/
-static void ParsePES( demux_t *p_demux, ts_pid_t *pid, block_t *p_pes )
+static void ParsePESDataChain( demux_t *p_demux, ts_pid_t *pid, block_t *p_pes )
 {
     uint8_t header[34];
     unsigned i_pes_size = 0;
@@ -1574,20 +1574,44 @@ static void ParsePES( demux_t *p_demux, ts_pid_t *pid, block_t *p_pes )
     }
 }
 
-static void ParsePESDataChain( demux_t *p_demux, ts_pid_t *pid )
+static bool PushPESBlock( demux_t *p_demux, ts_pid_t *pid, block_t *p_pkt, bool b_unit_start )
 {
-    block_t *p_datachain = pid->u.p_pes->p_data;
-    assert(p_datachain);
-    if(!p_datachain)
-        return;
+    bool b_ret = false;
+    ts_pes_t *p_pes = pid->u.p_pes;
+
+    if ( b_unit_start && p_pes->gather.p_data )
+    {
+        block_t *p_datachain = p_pes->gather.p_data;
+        /* Flush the pes from pid */
+        p_pes->gather.p_data = NULL;
+        p_pes->gather.i_data_size = 0;
+        p_pes->gather.i_gathered = 0;
+        p_pes->gather.pp_last = &pid->u.p_pes->gather.p_data;
+        ParsePESDataChain( p_demux, pid, p_datachain );
+        b_ret = true;
+    }
+
+    if( p_pkt == NULL )
+        return b_ret;
+
+    if( !b_unit_start && p_pes->gather.p_data == NULL )
+    {
+        /* msg_Dbg( p_demux, "broken packet" ); */
+        block_Release( p_pkt );
+    }
 
-    /* remove the pes from pid */
-    pid->u.p_pes->p_data = NULL;
-    pid->u.p_pes->i_data_size = 0;
-    pid->u.p_pes->i_data_gathered = 0;
-    pid->u.p_pes->pp_last = &pid->u.p_pes->p_data;
+    block_ChainLastAppend( &p_pes->gather.pp_last, p_pkt );
+    p_pes->gather.i_gathered += p_pkt->i_buffer;
 
-    ParsePES( p_demux, pid, p_datachain );
+    if( p_pes->gather.i_data_size > 0 &&
+        p_pes->gather.i_gathered >= p_pes->gather.i_data_size )
+    {
+        /* re-enter in Flush above */
+        assert(p_pes->gather.p_data);
+        return PushPESBlock( p_demux, pid, NULL, true );
+    }
+
+    return b_ret;
 }
 
 static block_t* ReadTSPacket( demux_t *p_demux )
@@ -1717,12 +1741,13 @@ static void UpdatePIDScrambledState( demux_t *p_demux, ts_pid_t *p_pid, bool b_s
 
 static inline void FlushESBuffer( ts_pes_t *p_pes )
 {
-    if( p_pes->p_data )
+    if( p_pes->gather.p_data )
     {
-        p_pes->i_data_gathered = p_pes->i_data_size = 0;
-        block_ChainRelease( p_pes->p_data );
-        p_pes->p_data = NULL;
-        p_pes->pp_last = &p_pes->p_data;
+        p_pes->gather.i_gathered = p_pes->gather.i_data_size = 0;
+        block_ChainRelease( p_pes->gather.p_data );
+        p_pes->gather.p_data = NULL;
+        p_pes->gather.pp_last = &p_pes->gather.p_data;
+        p_pes->gather.i_saved = 0;
     }
 
     if( p_pes->sl.p_data )
@@ -2085,7 +2110,7 @@ static void ProgramSetPCR( demux_t *p_demux, ts_pmt_t *p_pmt, mtime_t i_pcr )
 static int IsVideoEnd( ts_pid_t *p_pid )
 {
     /* jump to near end of PES packet */
-    block_t *p = p_pid->u.p_pes->p_data;
+    block_t *p = p_pid->u.p_pes->gather.p_data;
     if( !p || !p->p_next )
         return 0;
     while( p->p_next->p_next )
@@ -2113,20 +2138,22 @@ static void PCRCheckDTS( demux_t *p_demux, ts_pmt_t *p_pmt, mtime_t i_pcr)
         if( p_pid->type != TYPE_PES || SCRAMBLED(*p_pid) )
             continue;
 
-        if( p_pid->u.p_pes->p_data == NULL )
+        ts_pes_t *p_pes = p_pid->u.p_pes;
+        ts_pes_es_t *p_es = p_pes->p_es;
+
+        if( p_pes->gather.p_data == NULL )
             continue;
-        if( p_pid->u.p_pes->i_data_size != 0 )
+        if( p_pes->gather.i_data_size != 0 )
             continue;
 
         /* check only MPEG2, H.264 and VC-1 */
-        ts_pes_es_t *p_es = p_pid->u.p_pes->p_es;
         if( p_es->fmt.i_codec != VLC_CODEC_MPGV &&
             p_es->fmt.i_codec != VLC_CODEC_H264 &&
             p_es->fmt.i_codec != VLC_CODEC_VC1 )
             continue;
 
         uint8_t header[34];
-        const int i_max = block_ChainExtract( p_pid->u.p_pes->p_data, header, 34 );
+        const int i_max = block_ChainExtract( p_pes->gather.p_data, header, 34 );
 
         if( i_max < 6 || header[0] != 0 || header[1] != 0 || header[2] != 1 )
             continue;
@@ -2159,7 +2186,7 @@ static void PCRCheckDTS( demux_t *p_demux, ts_pmt_t *p_pmt, mtime_t i_pcr)
             {
                 msg_Warn( p_demux, "send queued data for pid %d: TS %"PRId64" <= PCR %"PRId64"\n",
                           p_pid->i_pid, i_dts > VLC_TS_INVALID ? i_dts : i_pts, i_pcr);
-                ParsePESDataChain( p_demux, p_pid );
+                PushPESBlock( p_demux, p_pid, NULL, true ); /* Flush */
             }
         }
     }
@@ -2287,6 +2314,9 @@ static bool ProcessTSPacket( demux_t *p_demux, ts_pid_t *pid, block_t *p_pkt )
     int         i_skip = 0;
     bool        b_ret  = false;
 
+   assert(pid->type == TYPE_PES);
+   ts_pes_t *p_pes = pid->u.p_pes;
+
 #if 0
     msg_Dbg( p_demux, "pid=%d unit_start=%d adaptation=%d payload=%d "
              "cc=0x%x", pid->i_pid, b_unit_start, b_adaptation,
@@ -2301,8 +2331,8 @@ static bool ProcessTSPacket( demux_t *p_demux, ts_pid_t *pid, block_t *p_pkt )
     {
         msg_Dbg( p_demux, "transport_error_indicator set (pid=%d)",
                  pid->i_pid );
-        if( pid->u.p_pes->p_data ) //&& pid->es->fmt.i_cat == VIDEO_ES )
-            pid->u.p_pes->p_data->i_flags |= BLOCK_FLAG_CORRUPTED;
+        if( p_pes->gather.p_data ) //&& pid->es->fmt.i_cat == VIDEO_ES )
+            p_pes->gather.p_data->i_flags |= BLOCK_FLAG_CORRUPTED;
     }
 
     if( SCRAMBLED(*pid) )
@@ -2333,7 +2363,7 @@ static bool ProcessTSPacket( demux_t *p_demux, ts_pid_t *pid, block_t *p_pkt )
         {
             /* discontinuity indicator found in stream */
             b_discontinuity = (p[5]&0x80) ? true : false;
-            if( b_discontinuity && pid->u.p_pes->p_data )
+            if( b_discontinuity && p_pes->gather.p_data )
             {
                 msg_Warn( p_demux, "discontinuity indicator (pid=%d) ",
                             pid->i_pid );
@@ -2372,12 +2402,13 @@ static bool ProcessTSPacket( demux_t *p_demux, ts_pid_t *pid, block_t *p_pkt )
                       i_cc, ( pid->i_cc + 1 )&0x0f, pid->i_pid );
 
             pid->i_cc = i_cc;
-            if( pid->u.p_pes->p_data && pid->u.p_pes->p_es->fmt.i_cat != VIDEO_ES &&
-                pid->u.p_pes->p_es->fmt.i_cat != AUDIO_ES )
+            if( p_pes->gather.p_data &&
+                p_pes->p_es->fmt.i_cat != VIDEO_ES &&
+                p_pes->p_es->fmt.i_cat != AUDIO_ES )
             {
                 /* Small audio/video artifacts are usually better than
                  * dropping full frames */
-                pid->u.p_pes->p_data->i_flags |= BLOCK_FLAG_CORRUPTED;
+                p_pes->gather.p_data->i_flags |= BLOCK_FLAG_CORRUPTED;
             }
         }
     }
@@ -2405,64 +2436,194 @@ static bool ProcessTSPacket( demux_t *p_demux, ts_pid_t *pid, block_t *p_pkt )
     }
 }
 
+/* Avoids largest memcpy */
+static bool block_Split( block_t **pp_block, block_t **pp_remain, size_t i_offset )
+{
+    block_t *p_block = *pp_block;
+    block_t *p_split = NULL;
+    *pp_remain = NULL;
+
+    size_t i_tocopy = p_block->i_buffer - i_offset;
+    if( i_tocopy > i_offset ) /* make new block for head */
+    {
+        if( i_offset > 0 )
+        {
+            p_split = block_Alloc( i_offset );
+            if( p_split == NULL )
+                return false;
+            memcpy( p_split->p_buffer, p_block->p_buffer, i_offset );
+            p_block->p_buffer += i_offset;
+            p_block->i_buffer -= i_offset;
+        }
+        *pp_remain = p_block;
+        *pp_block = p_split;
+    }
+    else /* other gets the tail of our split */
+    {
+        if( i_tocopy > 0 )
+        {
+            p_split = block_Alloc( i_tocopy );
+            if( p_split == NULL )
+                return false;
+            memcpy( p_split->p_buffer, &p_block->p_buffer[i_offset], i_tocopy );
+            p_block->i_buffer -= i_tocopy;
+        }
+        *pp_remain = p_split;
+    }
+    return true;
+}
+
+static uint8_t *FindNextPESHeader( uint8_t *p_buf, size_t i_buffer )
+{
+    const uint8_t *p_end = &p_buf[i_buffer];
+    unsigned i_bitflow = 0;
+    for( ; p_buf != p_end; p_buf++ )
+    {
+        i_bitflow <<= 1;
+        if( !*p_buf )
+        {
+            i_bitflow |= 1;
+        }
+        else if( *p_buf == 0x01 && (i_bitflow & 0x06) == 0x06 ) /* >= two zero prefixed 1 */
+        {
+            return p_buf - 2;
+        }
+    }
+    return NULL;
+}
+
+const uint8_t const pes_sync[] = { 0, 0, 1 };
+
+static bool MayHaveStartCodeOnEnd( const uint8_t *p_buf, size_t i_buf )
+{
+    assert(i_buf > 2);
+    return !( *(--p_buf) > 1 || *(--p_buf) > 0 || *(--p_buf) > 0 );
+}
+
 static bool GatherPESData( demux_t *p_demux, ts_pid_t *pid, block_t *p_pkt,
                            size_t i_skip, bool b_unit_start )
 {
-    bool i_ret = false;
+    bool b_ret = false;
+    ts_pes_t *p_pes = pid->u.p_pes;
 
     /* We have to gather it */
     p_pkt->p_buffer += i_skip;
     p_pkt->i_buffer -= i_skip;
 
-    if( b_unit_start )
-    {
-        if( pid->u.p_pes->p_data )
-        {
-            ParsePESDataChain( p_demux, pid );
-            i_ret = true;
-        }
+    bool b_single_payload = b_unit_start; /* Single payload in case of unit start */
+    bool b_aligned_ts_payload = true;
 
-        block_ChainLastAppend( &pid->u.p_pes->pp_last, p_pkt );
+    if( unlikely(p_pes->b_broken_PUSI_conformance) )
+    {
+        /* Stream does not conform to payload_unit_start flag
+         * applied to PES packets (AdTech private_stream_1) */
+        b_aligned_ts_payload = false;
+        b_single_payload = false;
 
-        if( p_pkt->i_buffer > 6 )
-        {
-            pid->u.p_pes->i_data_size = GetWBE( &p_pkt->p_buffer[4] );
-            if( pid->u.p_pes->i_data_size > 0 )
-            {
-                pid->u.p_pes->i_data_size += 6;
-            }
-        }
+    }
 
-        pid->u.p_pes->i_data_gathered += p_pkt->i_buffer;
-        if( pid->u.p_pes->i_data_size > 0 &&
-            pid->u.p_pes->i_data_gathered >= pid->u.p_pes->i_data_size )
+    if ( unlikely(p_pes->gather.i_saved > 0) )
+    {
+        /* Saved from previous packet end */
+        assert(p_pes->gather.i_saved < 6);
+        if( !b_aligned_ts_payload )
         {
-            ParsePESDataChain( p_demux, pid );
-            i_ret = true;
+            p_pkt = block_Realloc( p_pkt, p_pes->gather.i_saved, p_pkt->i_buffer );
+            if( p_pkt )
+                memcpy( p_pkt->p_buffer, p_pes->gather.saved, p_pes->gather.i_saved );
         }
+        p_pes->gather.i_saved = 0;
     }
-    else
+
+    for( bool b_first_sync_done = false; p_pkt; )
     {
-        if( pid->u.p_pes->p_data == NULL )
+        assert( p_pes->gather.i_saved == 0 );
+
+        if( p_pes->gather.p_data == NULL && !b_first_sync_done && p_pkt->i_buffer >= 6 )
         {
-            /* msg_Dbg( p_demux, "broken packet" ); */
-            block_Release( p_pkt );
+            if( likely(b_aligned_ts_payload) )
+            {
+                if( memcmp( p_pkt->p_buffer, pes_sync, 3 ) )
+                {
+                    block_Release( p_pkt );
+                    return b_ret;
+                }
+            }
+            else
+            {
+                /* Need to find sync code */
+                uint8_t *p_buf = FindNextPESHeader( p_pkt->p_buffer, p_pkt->i_buffer - 3 );
+                if( p_buf == NULL )
+                {
+                    /* no first sync code */
+                    if( MayHaveStartCodeOnEnd( p_pkt->p_buffer, p_pkt->i_buffer ) )
+                    {
+                        /* Drop everything except last bytes for next packet */
+                        p_pkt->p_buffer += p_pkt->i_buffer - 3;
+                        p_pes->gather.i_saved = p_pkt->i_buffer = 3;
+                        memcpy(p_pes->gather.saved, p_pkt->p_buffer, p_pkt->i_buffer);
+                    }
+                    block_Release( p_pkt );
+                    return b_ret;
+                }
+                p_pkt->i_buffer -= p_buf - p_pkt->p_buffer;
+                p_pkt->p_buffer = p_buf;
+            }
+            /* now points to PES header */
+            p_pes->gather.i_data_size = GetWBE(&p_pkt->p_buffer[4]);
+            if( p_pes->gather.i_data_size > 0 )
+                p_pes->gather.i_data_size += 6;
+            b_first_sync_done = true; /* Because if size is 0, we woud not look for second sync */
         }
         else
         {
-            block_ChainLastAppend( &pid->u.p_pes->pp_last, p_pkt );
-            pid->u.p_pes->i_data_gathered += p_pkt->i_buffer;
+            assert( p_pes->gather.i_data_size > p_pes->gather.i_gathered ||
+                    p_pes->gather.i_data_size == 0 );
 
-            if( pid->u.p_pes->i_data_size > 0 &&
-                pid->u.p_pes->i_data_gathered >= pid->u.p_pes->i_data_size )
+            /* If we started reading a fixed size */
+            if( p_pes->gather.i_data_size > p_pes->gather.i_gathered )
             {
-                ParsePESDataChain( p_demux, pid );
-                i_ret = true;
+                const size_t i_remain = p_pes->gather.i_data_size - p_pes->gather.i_gathered;
+                /* Append whole block */
+                if( likely(p_pkt->i_buffer <= i_remain || b_single_payload) )
+                {
+                    b_ret |= PushPESBlock( p_demux, pid, p_pkt, p_pes->gather.p_data == NULL );
+                    p_pkt = NULL;
+                }
+                else /* p_pkt->i_buffer > i_remain */
+                {
+                    block_t *p_split;
+                    if( !block_Split( &p_pkt, &p_split, i_remain ) )
+                    {
+                        block_Release( p_pkt );
+                        return false;
+                    }
+                    b_ret |= PushPESBlock( p_demux, pid, p_pkt, p_pes->gather.p_data == NULL );
+                    p_pkt = p_split;
+                    b_first_sync_done = false;
+                }
+            }
+            else /* if( p_pes->gather.i_data_size == 0 ) // see next packet */
+            {
+                /* Append or finish current/start new PES depending on unit_start */
+                b_ret |= PushPESBlock( p_demux, pid, p_pkt, b_unit_start );
+                p_pkt = NULL;
             }
         }
+
+        if( unlikely(p_pkt && p_pkt->i_buffer < 6) )
+        {
+            /* save and prepend to next packet */
+            assert(!b_single_payload);
+            assert(p_pes->gather.i_saved == 0);
+            p_pes->gather.i_saved = p_pkt->i_buffer;
+            memcpy(p_pes->gather.saved, p_pkt->p_buffer, p_pkt->i_buffer);
+            block_Release( p_pkt );
+            p_pkt = NULL;
+        }
     }
 
-    return i_ret;
+    return b_ret;
 }
 
 void TsChangeStandard( demux_sys_t *p_sys, ts_standards_e v )
diff --git a/modules/demux/mpeg/ts_streams.c b/modules/demux/mpeg/ts_streams.c
index 5080698..c015781 100644
--- a/modules/demux/mpeg/ts_streams.c
+++ b/modules/demux/mpeg/ts_streams.c
@@ -273,10 +273,11 @@ ts_pes_t *ts_pes_New( demux_t *p_demux, ts_pmt_t *p_program )
     }
     pes->i_stream_type = 0;
     pes->transport = TS_TRANSPORT_PES;
-    pes->i_data_size = 0;
-    pes->i_data_gathered = 0;
-    pes->p_data = NULL;
-    pes->pp_last = &pes->p_data;
+    pes->gather.i_data_size = 0;
+    pes->gather.i_gathered = 0;
+    pes->gather.p_data = NULL;
+    pes->gather.pp_last = &pes->gather.p_data;
+    pes->gather.i_saved = 0;
     pes->b_always_receive = false;
     pes->p_sections_proc = NULL;
     pes->p_prepcr_outqueue = NULL;
@@ -290,8 +291,8 @@ void ts_pes_Del( demux_t *p_demux, ts_pes_t *pes )
 {
     ts_pes_ChainDelete_es( p_demux, pes->p_es );
 
-    if( pes->p_data )
-        block_ChainRelease( pes->p_data );
+    if( pes->gather.p_data )
+        block_ChainRelease( pes->gather.p_data );
 
     if( pes->p_sections_proc )
         ts_sections_processor_ChainDelete( pes->p_sections_proc );
diff --git a/modules/demux/mpeg/ts_streams_private.h b/modules/demux/mpeg/ts_streams_private.h
index 25a8d55..16149da 100644
--- a/modules/demux/mpeg/ts_streams_private.h
+++ b/modules/demux/mpeg/ts_streams_private.h
@@ -107,11 +107,19 @@ struct ts_pes_t
     uint8_t i_stream_type;
 
     ts_transport_type_t transport;
-    int         i_data_size;
-    int         i_data_gathered;
-    block_t     *p_data;
-    block_t     **pp_last;
+
+    struct
+    {
+        size_t      i_data_size;
+        size_t      i_gathered;
+        block_t     *p_data;
+        block_t     **pp_last;
+        uint8_t     saved[5];
+        size_t      i_saved;
+    } gather;
+
     bool        b_always_receive;
+    bool        b_broken_PUSI_conformance;
     ts_sections_processor_t *p_sections_proc;
 
     block_t *   p_prepcr_outqueue;



More information about the vlc-commits mailing list