vlc | branch: master | Francois Cartegnie <[email protected]> | 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; _______________________________________________ vlc-commits mailing list [email protected] https://mailman.videolan.org/listinfo/vlc-commits
