[vlc-commits] demux: ts: fix pusi flag conformance
Francois Cartegnie
git at videolan.org
Wed Nov 9 17:28:21 CET 2016
vlc | branch: master | Francois Cartegnie <fcvlcdev at free.fr> | Tue Nov 8 20:08:30 2016 +0100| [48edcfab53d4dca0e10603a830c20878a8fa84ab] | 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=48edcfab53d4dca0e10603a830c20878a8fa84ab
---
modules/demux/mpeg/ts.c | 285 +++++++++++++++++++++++++-------
modules/demux/mpeg/ts_streams.c | 14 +-
modules/demux/mpeg/ts_streams_private.h | 16 +-
3 files changed, 243 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..862dc9f 100644
--- a/modules/demux/mpeg/ts_streams.c
+++ b/modules/demux/mpeg/ts_streams.c
@@ -273,10 +273,12 @@ 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_broken_PUSI_conformance = false;
pes->b_always_receive = false;
pes->p_sections_proc = NULL;
pes->p_prepcr_outqueue = NULL;
@@ -290,8 +292,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