Commit 141d5e26 authored by François Cartegnie's avatar François Cartegnie 🤞

demux: ts: add support for ETT tables

parent 5723baeb
......@@ -64,9 +64,13 @@
struct ts_psip_context_t
{
dvbpsi_atsc_mgt_t *p_mgt; /* Used to match (EITx,ETTx)<->PIDn */
dvbpsi_atsc_stt_t *p_stt; /* Time reference for EIT/EAS */
dvbpsi_atsc_vct_t *p_vct; /* Required for EIT vchannel -> program remapping */
atsc_a65_handle_t *p_a65; /* Shared Handle to avoid iconv reopens */
uint16_t i_tabletype; /* Only used by EIT/ETT pid */
DECL_ARRAY(dvbpsi_atsc_ett_t *) etts; /* For ETT pid, used on new EIT update */
DECL_ARRAY(dvbpsi_atsc_eit_t *) eits; /* For EIT pid, used on new ETT update */
};
ts_psip_context_t * ts_psip_context_New()
......@@ -74,24 +78,153 @@ ts_psip_context_t * ts_psip_context_New()
ts_psip_context_t *p_ctx = malloc(sizeof(*p_ctx));
if(likely(p_ctx))
{
p_ctx->p_mgt = NULL;
p_ctx->p_stt = NULL;
p_ctx->p_vct = NULL;
p_ctx->p_a65 = NULL;
p_ctx->i_tabletype = 0;
ARRAY_INIT(p_ctx->etts);
ARRAY_INIT(p_ctx->eits);
}
return p_ctx;
}
void ts_psip_context_Delete( ts_psip_context_t *p_ctx )
{
assert( !p_ctx->p_mgt || !p_ctx->etts.i_size );
assert( !p_ctx->p_vct || !p_ctx->eits.i_size );
if( p_ctx->p_mgt )
dvbpsi_atsc_DeleteMGT( p_ctx->p_mgt );
if( p_ctx->p_stt )
dvbpsi_atsc_DeleteSTT( p_ctx->p_stt );
if ( p_ctx->p_vct )
dvbpsi_atsc_DeleteVCT( p_ctx->p_vct );
if( p_ctx->p_a65 )
atsc_a65_handle_Release( p_ctx->p_a65 );
/* Things only used for ETT/EIT */
for( int i=0; i<p_ctx->etts.i_size; i++ )
dvbpsi_atsc_DeleteETT( p_ctx->etts.p_elems[i] );
for( int i=0; i<p_ctx->eits.i_size; i++ )
dvbpsi_atsc_DeleteEIT( p_ctx->eits.p_elems[i] );
ARRAY_RESET( p_ctx->etts );
ARRAY_RESET( p_ctx->eits );
free( p_ctx );
}
static ts_pid_t *ATSC_GetSiblingxTTPID( ts_pid_list_t *p_list, const dvbpsi_atsc_mgt_t *p_mgt, ts_psip_t *p_psip )
{
uint16_t i_lookup;
assert( p_psip->p_ctx->i_tabletype );
if( p_psip->p_ctx->i_tabletype >= ATSC_TABLE_TYPE_ETT_0 )
i_lookup = p_psip->p_ctx->i_tabletype - ATSC_TABLE_TYPE_ETT_0 + ATSC_TABLE_TYPE_EIT_0;
else
i_lookup = p_psip->p_ctx->i_tabletype - ATSC_TABLE_TYPE_EIT_0 + ATSC_TABLE_TYPE_ETT_0;
for( const dvbpsi_atsc_mgt_table_t *p_tab = p_mgt->p_first_table;
p_tab; p_tab = p_tab->p_next )
{
if( p_tab->i_table_type == i_lookup )
return ts_pid_Get( p_list, p_tab->i_table_type_pid );
}
return NULL;
}
static inline uint32_t toETMId( uint16_t i_vchannel, uint16_t i_event_id )
{
return (i_vchannel << 16) | (i_event_id << 2) | 0x02;
}
static inline void fromETMId( uint32_t i_etm_id, uint16_t *pi_vchannel, uint16_t *pi_event_id )
{
*pi_vchannel = i_etm_id >> 16;
*pi_event_id = (i_etm_id & 0xFFFF) >> 2;
}
static const dvbpsi_atsc_ett_t * ATSC_ETTFindByETMId( ts_psip_context_t *p_ettctx, uint32_t i_etm_id, uint8_t i_version )
{
int i;
ARRAY_BSEARCH( p_ettctx->etts, ->i_etm_id, uint32_t, i_etm_id, i );
if( i != -1 && p_ettctx->etts.p_elems[i]->i_version == i_version )
return p_ettctx->etts.p_elems[i];
return NULL;
}
static const dvbpsi_atsc_eit_event_t * ATSC_EventFindByETMId( ts_psip_context_t *p_eitctx,
uint32_t i_etm_id, uint8_t i_version )
{
uint16_t i_vchannel_id, i_event_id;
fromETMId( i_etm_id, &i_vchannel_id, &i_event_id );
for( int i=0; i<p_eitctx->eits.i_size; i++ )
{
dvbpsi_atsc_eit_t *p_eit = p_eitctx->eits.p_elems[i];
if( p_eit->i_version != i_version || p_eit->i_source_id != i_vchannel_id )
continue;
for( const dvbpsi_atsc_eit_event_t *p_evt = p_eit->p_first_event;
p_evt ; p_evt = p_evt->p_next )
{
if( p_evt->i_event_id == i_event_id )
return p_evt;
}
}
return NULL;
}
static void ATSC_EITInsert( ts_psip_context_t *p_ctx, dvbpsi_atsc_eit_t *p_eit )
{
for( int i=0; i<p_ctx->eits.i_size; i++ )
{
dvbpsi_atsc_eit_t *p_cur_eit = p_ctx->eits.p_elems[i];
if( p_cur_eit->i_source_id == p_eit->i_source_id )
{
dvbpsi_atsc_DeleteEIT( p_cur_eit ); /* Updated version */
p_ctx->eits.p_elems[i] = p_eit;
return;
}
}
ARRAY_APPEND( p_ctx->eits, p_eit );
}
static void ATSC_CleanETTByChannelVersion( ts_psip_context_t *p_ctx, uint16_t i_channel, uint8_t i_version )
{
int i=0;
while( i<p_ctx->etts.i_size )
{
dvbpsi_atsc_ett_t *p = p_ctx->etts.p_elems[i];
uint16_t i_curchan = p->i_etm_id >> 16;
if( i_channel < i_curchan )
break; /* because ordered */
if( i_curchan == i_channel && p->i_version != i_version )
{
dvbpsi_atsc_DeleteETT( p );
ARRAY_REMOVE( p_ctx->etts, i );
}
else i++;
}
}
static void ATSC_InsertETTOrdered( ts_psip_context_t *p_ctx, dvbpsi_atsc_ett_t *p_ett )
{
int i=0;
for( ; i<p_ctx->etts.i_size; i++ )
{
dvbpsi_atsc_ett_t *p = p_ctx->etts.p_elems[i];
if( p->i_etm_id >= p_ett->i_etm_id )
{
if( p->i_etm_id == p_ett->i_etm_id )
{
dvbpsi_atsc_DeleteETT( p );
p_ctx->etts.p_elems[i] = p_ett;
return;
}
break;
}
}
ARRAY_INSERT( p_ctx->etts, p_ett, i );
}
static bool ATSC_TranslateVChannelToProgram( const dvbpsi_atsc_vct_t *p_vct,
uint16_t i_channel, uint16_t *pi_program )
{
......@@ -163,6 +296,82 @@ static const char * ATSC_A53_get_service_type( uint8_t i_type )
} while(0);
#endif
static time_t ATSC_AddVLCEPGEvent( demux_t *p_demux, ts_psip_context_t *p_basectx,
const dvbpsi_atsc_eit_event_t *p_evt,
const dvbpsi_atsc_ett_t *p_ett,
vlc_epg_t *p_epg )
{
#ifndef ATSC_DEBUG_EIT
VLC_UNUSED(p_demux);
#endif
char *psz_title = atsc_a65_Decode_multiple_string( p_basectx->p_a65,
p_evt->i_title, p_evt->i_title_length );
char *psz_shortdesc_text = NULL;
char *psz_longdesc_text = NULL;
time_t i_start = atsc_a65_GPSTimeToEpoch( p_evt->i_start_time, p_basectx->p_stt->i_gps_utc_offset );
EIT_DEBUG_TIMESHIFT( i_start );
for( const dvbpsi_descriptor_t *p_dr = p_evt->p_first_descriptor;
p_dr; p_dr = p_dr->p_next )
{
switch( p_dr->i_tag )
{
case ATSC_DESCRIPTOR_CONTENT_ADVISORY:
{
const uint8_t *p_data = p_dr->p_data;
size_t i_data = p_dr->i_length;
uint8_t i_ratings_count = p_dr->p_data[0] & 0x3F;
p_data++; i_data--;
for( ; i_ratings_count && i_data > 3; i_ratings_count-- )
{
uint8_t i_rated_dimensions = p_data[1];
if( (size_t) i_rated_dimensions * 2 + 3 > i_data ) /* one more sanity check */
break;
uint8_t desclen = p_data[(size_t) 2 + 2 * i_rated_dimensions];
p_data += (size_t) 3 + 2 * i_rated_dimensions;
i_data -= (size_t) 3 + 2 * i_rated_dimensions;
if( desclen > i_data )
break;
if( unlikely(psz_shortdesc_text) )
free( psz_shortdesc_text );
psz_shortdesc_text = atsc_a65_Decode_multiple_string( p_basectx->p_a65, p_data, desclen );
if( psz_shortdesc_text ) /* Only keep first for now */
break;
p_data += desclen;
i_data -= desclen;
}
}
default:
break;
}
}
/* Try to match ETT */
if( p_ett )
{
psz_longdesc_text = atsc_a65_Decode_multiple_string( p_basectx->p_a65,
p_ett->p_etm_data, p_ett->i_etm_length );
}
if( i_start > VLC_TS_INVALID && psz_title )
{
#ifdef ATSC_DEBUG_EIT
msg_Dbg( p_demux, "EIT Event time %ld +%d %s id 0x%x",
i_start, p_evt->i_length_seconds, psz_title, p_evt->i_event_id );
#endif
vlc_epg_AddEvent( p_epg, i_start, p_evt->i_length_seconds,
psz_title, psz_shortdesc_text, psz_longdesc_text, 0 );
}
free( psz_title );
free( psz_shortdesc_text );
free( psz_longdesc_text );
return i_start;
}
static void ATSC_EIT_Callback( void *p_pid, dvbpsi_atsc_eit_t* p_eit )
{
ts_pid_t *p_eit_pid = (ts_pid_t *) p_pid;
......@@ -176,26 +385,29 @@ static void ATSC_EIT_Callback( void *p_pid, dvbpsi_atsc_eit_t* p_eit )
demux_t *p_demux = (demux_t *) p_eit_pid->u.p_psip->handle->p_sys;
ts_pid_t *p_base_pid = GetPID(p_demux->p_sys, ATSC_BASE_PID);
ts_psip_t *p_basepsip = p_base_pid->u.p_psip;
ts_psip_context_t *p_ctx = p_basepsip->p_ctx;
ts_psip_context_t *p_basectx = p_basepsip->p_ctx;
if( !p_eit->b_current_next ||
unlikely(p_base_pid->type != TYPE_PSIP || !p_ctx->p_stt || !p_ctx->p_vct) )
unlikely(p_base_pid->type != TYPE_PSIP || !p_basectx->p_stt || !p_basectx->p_vct) )
{
dvbpsi_atsc_DeleteEIT( p_eit );
return;
}
uint16_t i_program_number;
if ( !ATSC_TranslateVChannelToProgram( p_ctx->p_vct, p_eit->i_source_id, &i_program_number ) )
if ( !ATSC_TranslateVChannelToProgram( p_basectx->p_vct, p_eit->i_source_id, &i_program_number ) )
{
msg_Warn( p_demux, "Received EIT for unkown channel %d", p_eit->i_source_id );
dvbpsi_atsc_DeleteEIT( p_eit );
return;
}
const ts_pid_t *pid_sibling_ett = ATSC_GetSiblingxTTPID( &p_demux->p_sys->pids, p_basectx->p_mgt,
p_eit_pid->u.p_psip );
/* Get System Time for finding and setting current event */
time_t i_current_time = atsc_a65_GPSTimeToEpoch( p_ctx->p_stt->i_system_time,
p_ctx->p_stt->i_gps_utc_offset );
time_t i_current_time = atsc_a65_GPSTimeToEpoch( p_basectx->p_stt->i_system_time,
p_basectx->p_stt->i_gps_utc_offset );
EIT_DEBUG_TIMESHIFT( i_current_time );
......@@ -206,73 +418,26 @@ static void ATSC_EIT_Callback( void *p_pid, dvbpsi_atsc_eit_t* p_eit )
return;
}
if( !p_ctx->p_a65 && !(p_ctx->p_a65 = atsc_a65_handle_New( NULL )) )
if( !p_basectx->p_a65 && !(p_basectx->p_a65 = atsc_a65_handle_New( NULL )) )
goto end;
time_t i_current_event_start_time = 0;
for( const dvbpsi_atsc_eit_event_t *p_evt = p_eit->p_first_event;
p_evt ; p_evt = p_evt->p_next )
{
char *psz_title = atsc_a65_Decode_multiple_string( p_ctx->p_a65,
p_evt->i_title, p_evt->i_title_length );
char *psz_shortdesc_text = NULL;
/* Try to match ETT */
const dvbpsi_atsc_ett_t *p_ett = NULL;
if( pid_sibling_ett )
p_ett = ATSC_ETTFindByETMId( pid_sibling_ett->u.p_psip->p_ctx,
toETMId( p_eit->i_source_id, p_evt->i_event_id ),
p_eit->i_version );
time_t i_start = atsc_a65_GPSTimeToEpoch( p_evt->i_start_time, p_ctx->p_stt->i_gps_utc_offset );
EIT_DEBUG_TIMESHIFT( i_start );
/* Add Event to EPG based on EIT / available ETT */
time_t i_start = ATSC_AddVLCEPGEvent( p_demux, p_basectx, p_evt, p_ett, p_epg );
/* Try to find current event */
if( i_start <= i_current_time && i_start + p_evt->i_length_seconds > i_current_time )
i_current_event_start_time = i_start;
for( const dvbpsi_descriptor_t *p_dr = p_evt->p_first_descriptor;
p_dr; p_dr = p_dr->p_next )
{
switch( p_dr->i_tag )
{
case ATSC_DESCRIPTOR_CONTENT_ADVISORY:
{
const uint8_t *p_data = p_dr->p_data;
size_t i_data = p_dr->i_length;
uint8_t i_ratings_count = p_dr->p_data[0] & 0x3F;
p_data++; i_data--;
for( ; i_ratings_count && i_data > 3; i_ratings_count-- )
{
uint8_t i_rated_dimensions = p_data[1];
if( (size_t) i_rated_dimensions * 2 + 3 > i_data ) /* one more sanity check */
break;
uint8_t desclen = p_data[(size_t) 2 + 2 * i_rated_dimensions];
p_data += (size_t) 3 + 2 * i_rated_dimensions;
i_data -= (size_t) 3 + 2 * i_rated_dimensions;
if( desclen > i_data )
break;
if( unlikely(psz_shortdesc_text) )
free( psz_shortdesc_text );
psz_shortdesc_text = atsc_a65_Decode_multiple_string( p_ctx->p_a65, p_data, desclen );
if( psz_shortdesc_text ) /* Only keep first for now */
break;
p_data += desclen;
i_data -= desclen;
}
}
default:
break;
}
}
if( i_start > VLC_TS_INVALID && psz_title )
{
#ifdef ATSC_DEBUG_EIT
msg_Dbg( p_demux, "EIT Event vchannel/program %d/%d time %ld +%d %s",
p_eit->i_source_id, i_program_number, i_start, p_evt->i_length_seconds, psz_title );
#endif
vlc_epg_AddEvent( p_epg, i_start, p_evt->i_length_seconds,
psz_title, psz_shortdesc_text, NULL, 0 );
}
free( psz_title );
free( psz_shortdesc_text );
}
/* Update epg current time from system time ( required for pruning ) */
......@@ -284,7 +449,82 @@ static void ATSC_EIT_Callback( void *p_pid, dvbpsi_atsc_eit_t* p_eit )
end:
vlc_epg_Delete( p_epg );
dvbpsi_atsc_DeleteEIT( p_eit );
ATSC_EITInsert( p_eit_pid->u.p_psip->p_ctx, p_eit );
}
static void ATSC_ETT_Callback( void *p_pid, dvbpsi_atsc_ett_t *p_ett )
{
ts_pid_t *p_ett_pid = (ts_pid_t *) p_pid;
if( unlikely(p_ett_pid->type != TYPE_PSIP) )
{
assert( p_ett_pid->type == TYPE_PSIP );
dvbpsi_atsc_DeleteETT( p_ett );
return;
}
demux_t *p_demux = (demux_t *) p_ett_pid->u.p_psip->handle->p_sys;
ts_pid_t *p_base_pid = GetPID(p_demux->p_sys, ATSC_BASE_PID);
ts_psip_t *p_basepsip = p_base_pid->u.p_psip;
ts_psip_context_t *p_basectx = p_basepsip->p_ctx;
if( p_ett->i_etm_id & 0x02 ) /* Event ETT */
{
ts_psip_context_t *p_ctx = p_ett_pid->u.p_psip->p_ctx;
uint16_t i_vchannel_id, i_event_id;
fromETMId( p_ett->i_etm_id, &i_vchannel_id, &i_event_id );
uint16_t i_program_number;
if ( !ATSC_TranslateVChannelToProgram( p_basectx->p_vct, i_vchannel_id, &i_program_number ) )
{
msg_Warn( p_demux, "Received EIT for unkown channel %d", i_vchannel_id );
dvbpsi_atsc_DeleteETT( p_ett );
return;
}
/* If ETT with that version isn't already in list (inserted when matched eit is present) */
if( ATSC_ETTFindByETMId( p_ctx, p_ett->i_etm_id, p_ett->i_version ) == NULL )
{
const dvbpsi_atsc_mgt_t *p_mgt = ts_pid_Get( &p_demux->p_sys->pids, ATSC_BASE_PID )->u.p_psip->p_ctx->p_mgt;
ts_pid_t *p_sibling_eit = ATSC_GetSiblingxTTPID( &p_demux->p_sys->pids, p_mgt, p_ett_pid->u.p_psip );
if( p_sibling_eit )
{
const dvbpsi_atsc_eit_event_t *p_event =
ATSC_EventFindByETMId( p_sibling_eit->u.p_psip->p_ctx, p_ett->i_etm_id, p_ett->i_version );
if( p_event )
{
#ifdef ATSC_DEBUG_EIT
msg_Dbg( p_demux, "Should update EIT %x (matched EIT)", p_event->i_event_id );
#endif
vlc_epg_t *p_epg = vlc_epg_New( NULL );
if( likely(p_epg) )
{
(void)
ATSC_AddVLCEPGEvent( p_demux, p_basectx, p_event, p_ett, p_epg );
es_out_Control( p_demux->out, ES_OUT_SET_GROUP_EPG,
(int)i_program_number, p_epg );
#ifdef ATSC_DEBUG_EIT
msg_Dbg( p_demux, "Updated event %x with ETT", p_event->i_event_id );
#endif
vlc_epg_Delete( p_epg );
}
}
/* Insert to avoid duplicated event, and to be available to EIT if didn't appear yet */
ATSC_InsertETTOrdered( p_ctx, p_ett );
ATSC_CleanETTByChannelVersion( p_ctx, i_vchannel_id, p_ett->i_version );
return;
}
}
}
dvbpsi_atsc_DeleteETT( p_ett );
}
static void ATSC_ETT_RawCallback( dvbpsi_t *p_handle, const dvbpsi_psi_section_t* p_section,
void *p_base_pid )
{
VLC_UNUSED( p_handle );
dvbpsi_atsc_ett_t *p_ett = DVBPlague_ETT_Decode( p_section );
if( p_ett ) /* Send to real callback */
ATSC_ETT_Callback( p_base_pid, p_ett );
}
static void ATSC_VCT_Callback( void *p_cb_basepid, dvbpsi_atsc_vct_t* p_vct )
......@@ -410,6 +650,9 @@ static void ATSC_MGT_Callback( void *p_cb_basepid, dvbpsi_atsc_mgt_t* p_mgt )
}
}
if( p_mgtpsip->p_ctx->p_mgt )
dvbpsi_atsc_DeleteMGT( p_mgtpsip->p_ctx->p_mgt );
p_mgtpsip->p_ctx->p_mgt = p_mgt;
p_mgtpsip->i_version = p_mgt->i_version;
for( const dvbpsi_atsc_mgt_table_t *p_tab = p_mgt->p_first_table;
......@@ -434,11 +677,27 @@ static void ATSC_MGT_Callback( void *p_cb_basepid, dvbpsi_atsc_mgt_t* p_mgt )
if( PIDSetup( p_demux, TYPE_PSIP, pid, NULL ) )
{
SetPIDFilter( p_demux->p_sys, pid, true );
pid->u.p_psip->p_ctx->i_tabletype = p_tab->i_table_type;
ATSC_Ready_SubDecoders( pid->u.p_psip->handle, pid );
msg_Dbg( p_demux, " * pid=%d reserved for ATSC EIT", pid->i_pid );
ARRAY_APPEND( p_mgtpsip->eit, pid );
}
}
else if( p_tab->i_table_type >= ATSC_TABLE_TYPE_ETT_0 &&
p_tab->i_table_type <= ATSC_TABLE_TYPE_ETT_0 + ATSC_EIT_MAX_DEPTH_MIN1 &&
p_tab->i_table_type <= ATSC_TABLE_TYPE_ETT_127 &&
p_tab->i_table_type_pid != p_base_pid->i_pid )
{
ts_pid_t *pid = GetPID(p_demux->p_sys, p_tab->i_table_type_pid);
if( PIDSetup( p_demux, TYPE_PSIP, pid, NULL ) )
{
SetPIDFilter( p_demux->p_sys, pid, true );
pid->u.p_psip->p_ctx->i_tabletype = p_tab->i_table_type;
ATSC_Ready_SubDecoders( pid->u.p_psip->handle, pid );
msg_Dbg( p_demux, " * pid=%d reserved for ATSC ETT", pid->i_pid );
ARRAY_APPEND( p_mgtpsip->eit, pid );
}
}
msg_Dbg( p_demux, " * pid=%d transport for ATSC PSIP type %x",
p_tab->i_table_type_pid, p_tab->i_table_type );
}
......@@ -449,8 +708,6 @@ static void ATSC_MGT_Callback( void *p_cb_basepid, dvbpsi_atsc_mgt_t* p_mgt )
{
msg_Dbg( p_demux, " * pid=%d listening for EAS", p_base_pid->i_pid );
}
dvbpsi_atsc_DeleteMGT( p_mgt );
}
static void ATSC_STT_Callback( void *p_cb_basepid, dvbpsi_atsc_stt_t* p_stt )
......@@ -509,12 +766,16 @@ static void ATSC_NewTable_Callback( dvbpsi_t *p_dvbpsi, uint8_t i_table_id,
{
demux_t *p_demux = (demux_t *) p_dvbpsi->p_sys;
assert( ((ts_pid_t *) p_cb_pid)->type == TYPE_PSIP );
const ts_pid_t *p_base_pid = GetPID(p_demux->p_sys, ATSC_BASE_PID);
const ts_pid_t *p_base_pid = ts_pid_Get( &p_demux->p_sys->pids, ATSC_BASE_PID );
if( !p_base_pid->u.p_psip->p_ctx->p_vct )
return;
switch( i_table_id )
{
case ATSC_ETT_TABLE_ID:
ATSC_ATTACH_WITH_FIXED_DECODER( p_dvbpsi, ETT, ATSC_ETT_TABLE_ID, i_extension, p_cb_pid );
break;
case ATSC_EIT_TABLE_ID:
ATSC_ATTACH( p_dvbpsi, EIT, ATSC_EIT_TABLE_ID, i_extension, p_cb_pid );
break;
......
......@@ -125,6 +125,9 @@ struct ts_psip_t
int i_version;
ts_pes_es_t *p_eas_es;
ts_psip_context_t *p_ctx;
/* Used to track list of active pid for eit/ett, to call PIDRelease on them.
VCT table could have been used, but PIDSetup can fail, and we can't alter
the VCT table accordingly without going ahead of more troubles */
DECL_ARRAY(ts_pid_t *) eit;
};
......
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