/*****************************************************************************
* ts_streams.c: Transport Stream input module for VLC.
*****************************************************************************
* Copyright (C) 2004-2016 VLC authors and VideoLAN
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include
#include "ts_pid.h"
#include "ts_streams.h"
#include "ts_streams_private.h"
#ifndef _DVBPSI_DVBPSI_H_
#include
#endif
#ifndef _DVBPSI_DEMUX_H_
#include
#endif
#include
#include
#include
#include "../../mux/mpeg/dvbpsi_compat.h" /* dvbpsi_messages */
#include
#include
#include
#include
#include "sections.h"
#include "ts_pid.h"
#include "ts.h"
#include "ts_psip.h"
static inline bool handle_Init( demux_t *p_demux, dvbpsi_t **handle )
{
*handle = dvbpsi_new( &dvbpsi_messages, DVBPSI_MSG_DEBUG );
if( !*handle )
return false;
(*handle)->p_sys = (void *) p_demux;
return true;
}
ts_pat_t *ts_pat_New( demux_t *p_demux )
{
ts_pat_t *pat = malloc( sizeof( ts_pat_t ) );
if( !pat )
return NULL;
if( !handle_Init( p_demux, &pat->handle ) )
{
free( pat );
return NULL;
}
pat->i_version = -1;
pat->i_ts_id = -1;
pat->b_generated = false;
ARRAY_INIT( pat->programs );
return pat;
}
void ts_pat_Del( demux_t *p_demux, ts_pat_t *pat )
{
if( dvbpsi_decoder_present( pat->handle ) )
dvbpsi_pat_detach( pat->handle );
dvbpsi_delete( pat->handle );
for( int i=0; iprograms.i_size; i++ )
PIDRelease( p_demux, pat->programs.p_elems[i] );
ARRAY_RESET( pat->programs );
free( pat );
}
ts_pmt_t *ts_pat_Get_pmt( ts_pat_t *pat, uint16_t i_number )
{
ts_pmt_t *p_pmt = NULL;
for( int i=0; iprograms.i_size; i++ )
{
p_pmt = pat->programs.p_elems[i]->u.p_pmt;
if( p_pmt->i_number == i_number )
break;
}
return p_pmt;
}
ts_pmt_t *ts_pmt_New( demux_t *p_demux )
{
ts_pmt_t *pmt = malloc( sizeof( ts_pmt_t ) );
if( !pmt )
return NULL;
if( !handle_Init( p_demux, &pmt->handle ) )
{
free( pmt );
return NULL;
}
ARRAY_INIT( pmt->e_streams );
pmt->i_version = -1;
pmt->i_number = -1;
pmt->i_pid_pcr = 0x1FFF;
pmt->b_selected = false;
pmt->iod = NULL;
pmt->od.i_version = -1;
ARRAY_INIT( pmt->od.objects );
pmt->i_last_dts = -1;
pmt->i_last_dts_byte = 0;
pmt->p_atsc_si_basepid = NULL;
pmt->p_si_sdt_pid = NULL;
pmt->pcr.i_current = -1;
pmt->pcr.i_first = -1;
pmt->pcr.b_disable = false;
pmt->pcr.i_first_dts = VLC_TICK_INVALID;
pmt->pcr.i_pcroffset = -1;
pmt->pcr.b_fix_done = false;
pmt->eit.i_event_length = 0;
pmt->eit.i_event_start = 0;
pmt->arib.i_download_id = -1;
pmt->arib.i_logo_id = -1;
return pmt;
}
void ts_pmt_Del( demux_t *p_demux, ts_pmt_t *pmt )
{
if( dvbpsi_decoder_present( pmt->handle ) )
dvbpsi_pmt_detach( pmt->handle );
dvbpsi_delete( pmt->handle );
for( int i=0; ie_streams.i_size; i++ )
PIDRelease( p_demux, pmt->e_streams.p_elems[i] );
ARRAY_RESET( pmt->e_streams );
if( pmt->p_atsc_si_basepid )
PIDRelease( p_demux, pmt->p_atsc_si_basepid );
if( pmt->p_si_sdt_pid )
PIDRelease( p_demux, pmt->p_si_sdt_pid );
if( pmt->iod )
ODFree( pmt->iod );
for( int i=0; iod.objects.i_size; i++ )
ODFree( pmt->od.objects.p_elems[i] );
ARRAY_RESET( pmt->od.objects );
if( pmt->i_number > -1 )
es_out_Control( p_demux->out, ES_OUT_DEL_GROUP, pmt->i_number );
free( pmt );
}
ts_es_t * ts_es_New( ts_pmt_t *p_program )
{
ts_es_t *p_es = malloc( sizeof(*p_es) );
if( p_es )
{
p_es->p_program = p_program;
p_es->id = NULL;
p_es->i_sl_es_id = 0;
p_es->i_next_block_flags = 0;
p_es->p_extraes = NULL;
p_es->p_next = NULL;
p_es->b_interlaced = false;
es_format_Init( &p_es->fmt, UNKNOWN_ES, 0 );
p_es->fmt.i_group = p_program->i_number;
p_es->metadata.i_format = 0;
p_es->metadata.i_service_id = 0;
}
return p_es;
}
static void ts_pes_es_Clean( demux_t *p_demux, ts_es_t *p_es )
{
if( p_es->id )
{
/* Ensure we don't wait for overlap hacks #14257 */
es_out_Control( p_demux->out, ES_OUT_SET_ES_STATE, p_es->id, false );
es_out_Del( p_demux->out, p_es->id );
p_demux->p_sys->i_pmt_es--;
}
es_format_Clean( &p_es->fmt );
}
void ts_stream_Add_es( ts_stream_t *p_pes, ts_es_t *p_es, bool b_extra )
{
ts_es_t **pp_es = (b_extra && p_pes->p_es) ? /* Ensure extra has main es */
&p_pes->p_es->p_extraes :
&p_pes->p_es;
if( likely(!*pp_es) )
{
*pp_es = p_es;
}
else
{
ts_es_t *p_next = (*pp_es)->p_next;
(*pp_es)->p_next = p_es;
p_es->p_next = p_next;
}
}
ts_es_t * ts_stream_Find_es( ts_stream_t *p_pes, const ts_pmt_t *p_pmt )
{
for( ts_es_t *p_es = p_pes->p_es; p_es; p_es = p_es->p_next )
{
if( p_es->p_program == p_pmt )
return p_es;
}
return NULL;
}
ts_es_t * ts_stream_Extract_es( ts_stream_t *p_pes, const ts_pmt_t *p_pmt )
{
ts_es_t **pp_prev = &p_pes->p_es;
for( ts_es_t *p_es = p_pes->p_es; p_es; p_es = p_es->p_next )
{
if( p_es->p_program == p_pmt )
{
*pp_prev = p_es->p_next;
p_es->p_next = NULL;
return p_es;
}
pp_prev = &p_es->p_next;
}
return NULL;
}
size_t ts_Count_es( const ts_es_t *p_es, bool b_active, const ts_pmt_t *p_pmt )
{
size_t i=0;
for( ; p_es; p_es = p_es->p_next )
{
i += ( b_active ) ? !!p_es->id : ( ( !p_pmt || p_pmt == p_es->p_program ) ? 1 : 0 );
i += ts_Count_es( p_es->p_extraes, b_active, p_pmt );
}
return i;
}
static void ts_pes_ChainDelete_es( demux_t *p_demux, ts_es_t *p_es )
{
while( p_es )
{
ts_es_t *p_next = p_es->p_next;
ts_pes_ChainDelete_es( p_demux, p_es->p_extraes );
ts_pes_es_Clean( p_demux, p_es );
free( p_es );
p_es = p_next;
}
}
ts_stream_t *ts_stream_New( demux_t *p_demux, ts_pmt_t *p_program )
{
VLC_UNUSED(p_demux);
ts_stream_t *pes = malloc( sizeof( ts_stream_t ) );
if( !pes )
return NULL;
pes->p_es = ts_es_New( p_program );
if( !pes->p_es )
{
free( pes );
return NULL;
}
pes->i_stream_type = 0;
pes->transport = TS_TRANSPORT_PES;
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->gather.i_append_pcr = VLC_TICK_INVALID;
pes->b_broken_PUSI_conformance = false;
pes->b_always_receive = false;
pes->p_sections_proc = NULL;
pes->p_proc = NULL;
pes->prepcr.p_head = NULL;
pes->prepcr.pp_last = &pes->prepcr.p_head;
return pes;
}
void ts_stream_Del( demux_t *p_demux, ts_stream_t *pes )
{
ts_pes_ChainDelete_es( p_demux, pes->p_es );
if( pes->gather.p_data )
block_ChainRelease( pes->gather.p_data );
if( pes->p_sections_proc )
ts_sections_processor_ChainDelete( pes->p_sections_proc );
if( pes->p_proc )
ts_stream_processor_Delete( pes->p_proc );
if( pes->prepcr.p_head )
block_ChainRelease( pes->prepcr.p_head );
free( pes );
}
ts_si_t *ts_si_New( demux_t *p_demux )
{
ts_si_t *si = malloc( sizeof( ts_si_t ) );
if( !si )
return NULL;
if( !handle_Init( p_demux, &si->handle ) )
{
free( si );
return NULL;
}
si->i_version = -1;
si->eitpid = NULL;
si->tdtpid = NULL;
si->cdtpid = NULL;
return si;
}
void ts_si_Del( demux_t *p_demux, ts_si_t *si )
{
if( dvbpsi_decoder_present( si->handle ) )
dvbpsi_DetachDemux( si->handle );
dvbpsi_delete( si->handle );
if( si->eitpid )
PIDRelease( p_demux, si->eitpid );
if( si->tdtpid )
PIDRelease( p_demux, si->tdtpid );
if( si->cdtpid )
PIDRelease( p_demux, si->cdtpid );
free( si );
}
void ts_psip_Del( demux_t *p_demux, ts_psip_t *psip )
{
if( psip->p_ctx )
ts_psip_context_Delete( psip->p_ctx );
ts_pes_ChainDelete_es( p_demux, psip->p_eas_es );
if( psip->handle )
{
ATSC_Detach_Dvbpsi_Decoders( psip->handle );
dvbpsi_delete( psip->handle );
}
for( int i=0; ieit.i_size; i++ )
PIDRelease( p_demux, psip->eit.p_elems[i] );
ARRAY_RESET( psip->eit );
free( psip );
}
ts_psip_t *ts_psip_New( demux_t *p_demux )
{
ts_psip_t *psip = malloc( sizeof( ts_psip_t ) );
if( !psip )
return NULL;
if( !handle_Init( p_demux, &psip->handle ) )
{
free( psip );
return NULL;
}
ARRAY_INIT( psip->eit );
psip->i_version = -1;
psip->p_eas_es = NULL;
psip->p_ctx = ts_psip_context_New();
if( !psip->p_ctx )
{
ts_psip_Del( p_demux, psip );
psip = NULL;
}
return psip;
}