Commit 48edcfab authored by François Cartegnie's avatar François 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.
parent 46894911
......@@ -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 )
......
......@@ -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 );
......
......@@ -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;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment