/***************************************************************************** * ts_psip.c : TS demux ATSC A65 PSIP handling ***************************************************************************** * Copyright (C) 2016 - VideoLAN Authors * * 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 #include #include #ifndef _DVBPSI_DVBPSI_H_ #include #endif #ifndef _DVBPSI_DEMUX_H_ #include #endif #include #include #include #include #include #include #include /* Custom decoders */ #include #include "ts_decoders.h" #include "ts_psip_dvbpsi_fixes.h" #include "ts_pid.h" #include "ts.h" #include "ts_streams_private.h" #include "ts_scte.h" #include "ts_psip.h" #include "../codec/atsc_a65.h" #include "../codec/scte18.h" #include static inline char *grab_notempty( char **ppsz ) { char *psz_ret = NULL; if( *ppsz && **ppsz ) { psz_ret = *ppsz; *ppsz = NULL; } return psz_ret; } /* * Decoders activation order due to dependencies, * and because callbacks will be fired once per MGT/VCT version * STT(ref by EIT,EAS) -> MGT * MGT -> VCT,EAS,EIT/ETT */ 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 */ }; void ts_psip_Packet_Push( ts_pid_t *p_pid, const uint8_t *p_pktbuffer ) { if( p_pid->u.p_psip->handle->p_decoder && likely(p_pid->type == TYPE_PSIP) ) dvbpsi_packet_push( p_pid->u.p_psip->handle, (uint8_t *) p_pktbuffer ); } 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; ietts.i_size; i++ ) dvbpsi_atsc_DeleteETT( p_ctx->etts.p_elems[i] ); for( int i=0; ieits.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; ieits.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; ieits.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( ietts.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( ; ietts.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 ) { for( const dvbpsi_atsc_vct_channel_t *p_channel = p_vct->p_first_channel; p_channel; p_channel = p_channel->p_next ) { if( p_channel->i_source_id == i_channel ) { *pi_program = p_channel->i_program_number; return true; } } return false; } static void ATSC_NewTable_Callback( dvbpsi_t *p_dvbpsi, uint8_t i_table_id, uint16_t i_extension, void *p_pid ); /* Just Hook a base demux, and let NewTableCallback handle decoders creation */ static bool ATSC_Ready_SubDecoders( dvbpsi_t *p_handle, void *p_cb_pid ) { if( !dvbpsi_decoder_present( p_handle ) ) return dvbpsi_AttachDemux( p_handle, ATSC_NewTable_Callback, p_cb_pid ); return true; } void ATSC_Detach_Dvbpsi_Decoders( dvbpsi_t *p_handle ) { if( dvbpsi_decoder_present( p_handle ) ) dvbpsi_DetachDemux( p_handle ); } #define ATSC_ATTACH( handle, type, table, extension, pid ) \ ( ATSC_Ready_SubDecoders( handle, pid ) &&\ ( dvbpsi_demuxGetSubDec( (dvbpsi_demux_t *) handle->p_decoder, table, extension ) ||\ dvbpsi_atsc_Attach ## type( handle, table, extension, ATSC_ ## type ## _Callback, pid ) ) ) #define ATSC_ATTACH_WITH_FIXED_DECODER( handle, type, table, extension, pid ) \ ( ATSC_Ready_SubDecoders( handle, pid ) &&\ ( dvbpsi_demuxGetSubDec( (dvbpsi_demux_t *) handle->p_decoder, table, extension ) ||\ ts_dvbpsi_AttachRawSubDecoder( handle, table, extension, ATSC_ ## type ## _RawCallback, pid ) ) ) static const char * const rgpsz_ATSC_A53_service_types[] = { "Analog Television", "ATSC Digital Television", "ATSC Audio", "ATSC Data Only Service", "ATSC Software Download Service", }; static const char * ATSC_A53_get_service_type( uint8_t i_type ) { if( i_type == 0 || i_type > 5 ) return NULL; return rgpsz_ATSC_A53_service_types[i_type - 1]; } #ifndef ATSC_DEBUG_EIT #define EIT_DEBUG_TIMESHIFT(t) #else /* Define static time var used as anchor to current time to offset all eit entries */ static time_t i_eit_debug_offset = 0; #define EIT_DEBUG_TIMESHIFT(t) \ do {\ if( i_eit_debug_offset == 0 )\ i_eit_debug_offset = time(NULL) - t;\ t = t + i_eit_debug_offset;\ } while(0); #endif static vlc_epg_event_t * ATSC_CreateVLCEPGEvent( 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 ) { #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; vlc_epg_event_t *p_epgevt = 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_TICK_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 p_epgevt = vlc_epg_event_New( p_evt->i_event_id, i_start, p_evt->i_length_seconds ); if( p_epgevt ) { p_epgevt->psz_name = grab_notempty( &psz_title ); p_epgevt->psz_short_description = grab_notempty( &psz_shortdesc_text ); p_epgevt->psz_description = grab_notempty( &psz_longdesc_text ); } } free( psz_title ); free( psz_shortdesc_text ); free( psz_longdesc_text ); return p_epgevt; } static time_t ATSC_AddVLCEPGEvent( demux_t *p_demux, ts_psip_context_t *p_basectx, const dvbpsi_atsc_eit_event_t *p_event, const dvbpsi_atsc_ett_t *p_ett, vlc_epg_t *p_epg ) { vlc_epg_event_t *p_evt = ATSC_CreateVLCEPGEvent( p_demux, p_basectx, p_event, p_ett ); if( p_evt ) { if( vlc_epg_AddEvent( p_epg, p_evt ) ) return p_evt->i_start; vlc_epg_event_Delete( p_evt ); } return VLC_TICK_INVALID; } 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; if( unlikely(p_eit_pid->type != TYPE_PSIP) ) { assert( p_eit_pid->type == TYPE_PSIP ); dvbpsi_atsc_DeleteEIT( p_eit ); return; } 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_basectx = p_basepsip->p_ctx; if( !p_eit->b_current_next || 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_basectx->p_vct, p_eit->i_source_id, &i_program_number ) ) { msg_Warn( p_demux, "Received EIT for unknown 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_basectx->p_stt->i_system_time, p_basectx->p_stt->i_gps_utc_offset ); EIT_DEBUG_TIMESHIFT( i_current_time ); const uint16_t i_table_type = p_eit_pid->u.p_psip->p_ctx->i_tabletype; assert(i_table_type); /* Use PID for segmenting our EPG tables updates. 1 EIT/PID transmits 3 hours, * with a max of 16 days over 128 EIT/PID. Unlike DVD, table ID is here fixed. * see ATSC A/65 5.0 */ vlc_epg_t *p_epg = vlc_epg_New( i_table_type - ATSC_TABLE_TYPE_EIT_0, i_program_number ); if( !p_epg ) { dvbpsi_atsc_DeleteEIT( p_eit ); return; } /* Use first table as present/following (not split like DVB) */ p_epg->b_present = (i_table_type == ATSC_TABLE_TYPE_EIT_0); 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 ) { /* 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 ); /* 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; } /* Update epg current time from system time ( required for pruning ) */ if( p_epg->b_present && i_current_event_start_time ) { vlc_epg_SetCurrent( p_epg, i_current_event_start_time ); ts_pat_t *p_pat = ts_pid_Get(&p_demux->p_sys->pids, 0)->u.p_pat; ts_pmt_t *p_pmt = ts_pat_Get_pmt(p_pat, i_program_number); if(p_pmt) { p_pmt->eit.i_event_start = p_epg->p_current->i_start; p_pmt->eit.i_event_length = p_epg->p_current->i_duration; } } if( p_epg->i_event > 0 ) es_out_Control( p_demux->out, ES_OUT_SET_GROUP_EPG, (int)i_program_number, p_epg ); end: vlc_epg_Delete( p_epg ); 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 unknown 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_event_t *p_evt = ATSC_CreateVLCEPGEvent( p_demux, p_basectx, p_event, p_ett ); if( likely(p_evt) ) { es_out_Control( p_demux->out, ES_OUT_SET_GROUP_EPG_EVENT, (int)i_program_number, p_evt ); #ifdef ATSC_DEBUG_EIT msg_Dbg( p_demux, "Updated event %x with ETT", p_evt->i_id ); #endif vlc_epg_event_Delete( p_evt ); } } /* 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 ); for( ; p_section; p_section = p_section->p_next ) { 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 ) { ts_pid_t *p_base_pid = (ts_pid_t *) p_cb_basepid; if( unlikely(p_base_pid->type != TYPE_PSIP || p_base_pid->i_pid != ATSC_BASE_PID) ) { assert( p_base_pid->type == TYPE_PSIP ); assert( p_base_pid->i_pid == ATSC_BASE_PID ); dvbpsi_atsc_DeleteVCT( p_vct ); return; } demux_t *p_demux = (demux_t *) p_base_pid->u.p_psip->handle->p_sys; ts_psip_context_t *p_ctx = p_base_pid->u.p_psip->p_ctx; if( !p_ctx->p_a65 && !(p_ctx->p_a65 = atsc_a65_handle_New( NULL )) ) goto end; for( const dvbpsi_atsc_vct_channel_t *p_channel = p_vct->p_first_channel; p_channel; p_channel = p_channel->p_next ) { vlc_meta_t *p_meta = vlc_meta_New(); if( p_meta ) { char *psz_name = NULL; for( dvbpsi_descriptor_t *p_dr = p_channel->p_first_descriptor; p_dr; p_dr = p_dr->p_next ) { switch( p_dr->i_tag ) { case ATSC_DESCRIPTOR_EXTENDED_CHANNEL_NAME: { dvbpsi_extended_channel_name_dr_t *p_ecndr = dvbpsi_ExtendedChannelNameDr( p_dr ); if( p_ecndr ) { if( unlikely(psz_name) ) free( psz_name ); psz_name = atsc_a65_Decode_multiple_string( p_ctx->p_a65, p_ecndr->i_long_channel_name, p_ecndr->i_long_channel_name_length ); } } break; default: break; } } if( !psz_name ) psz_name = atsc_a65_Decode_simple_UTF16_string( p_ctx->p_a65, p_channel->i_short_name, 14 ); if( psz_name ) { vlc_meta_SetTitle( p_meta, psz_name ); free( psz_name ); } const char *psz_service_type = ATSC_A53_get_service_type( p_channel->i_service_type ); if( psz_service_type ) vlc_meta_AddExtra( p_meta, "Type", psz_service_type ); es_out_Control( p_demux->out, ES_OUT_SET_GROUP_META, p_channel->i_program_number, p_meta ); vlc_meta_Delete( p_meta ); } } end: if( p_ctx->p_vct ) dvbpsi_atsc_DeleteVCT( p_ctx->p_vct ); p_ctx->p_vct = p_vct; } static void ATSC_MGT_Callback( void *p_cb_basepid, dvbpsi_atsc_mgt_t* p_mgt ) { ts_pid_t *p_base_pid = (ts_pid_t *) p_cb_basepid; if( unlikely(p_base_pid->type != TYPE_PSIP || p_base_pid->i_pid != ATSC_BASE_PID) ) { assert( p_base_pid->type == TYPE_PSIP ); assert( p_base_pid->i_pid == ATSC_BASE_PID ); dvbpsi_atsc_DeleteMGT( p_mgt ); return; } ts_psip_t *p_mgtpsip = p_base_pid->u.p_psip; demux_t *p_demux = (demux_t *) p_mgtpsip->handle->p_sys; if( ( p_mgtpsip->i_version != -1 && p_mgtpsip->i_version == p_mgt->i_version ) || p_mgt->b_current_next == 0 ) { dvbpsi_atsc_DeleteMGT( p_mgt ); return; } /* Easy way, delete and recreate every child if any new version comes * (We don't need to keep PID active as with video/PMT update) */ if( p_mgtpsip->i_version != -1 ) { if( p_mgtpsip->p_ctx->p_vct ) { dvbpsi_atsc_DeleteVCT( p_mgtpsip->p_ctx->p_vct ); p_mgtpsip->p_ctx->p_vct = NULL; } /* Remove EIT/ETT */ for( int i=0; i < p_mgtpsip->eit.i_size; i++ ) { PIDRelease( p_demux, p_mgtpsip->eit.p_elems[i] ); assert( p_mgtpsip->eit.p_elems[i]->type == TYPE_FREE ); } ARRAY_RESET(p_mgtpsip->eit); /* Remove EAS */ dvbpsi_demux_t *p_dvbpsi_demux = (dvbpsi_demux_t *) p_mgtpsip->handle->p_decoder; dvbpsi_demux_subdec_t *p_subdec = dvbpsi_demuxGetSubDec( p_dvbpsi_demux, SCTE18_TABLE_ID, 0x00 ); if( p_subdec ) { dvbpsi_DetachDemuxSubDecoder( p_dvbpsi_demux, p_subdec ); dvbpsi_DeleteDemuxSubDecoder( p_subdec ); } } 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; p_tab; p_tab = p_tab->p_next ) { if( p_tab->i_table_type == ATSC_TABLE_TYPE_TVCT || p_tab->i_table_type == ATSC_TABLE_TYPE_CVCT ) { const uint8_t i_table_id = (p_tab->i_table_type == ATSC_TABLE_TYPE_CVCT) ? ATSC_CVCT_TABLE_ID : ATSC_TVCT_TABLE_ID; if( !ATSC_ATTACH( p_mgtpsip->handle, VCT, i_table_id, GetPID(p_demux->p_sys, 0)->u.p_pat->i_ts_id, p_base_pid ) ) msg_Dbg( p_demux, " * pid=%d listening for ATSC VCT", p_base_pid->i_pid ); } else if( p_tab->i_table_type >= ATSC_TABLE_TYPE_EIT_0 && p_tab->i_table_type <= ATSC_TABLE_TYPE_EIT_0 + ATSC_EIT_MAX_DEPTH_MIN1 && p_tab->i_table_type <= ATSC_TABLE_TYPE_EIT_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 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 ); } if( SCTE18_SI_BASE_PID == ATSC_BASE_PID && ts_dvbpsi_AttachRawSubDecoder( p_mgtpsip->handle, SCTE18_TABLE_ID, 0x00, SCTE18_Section_Callback, p_base_pid ) ) { msg_Dbg( p_demux, " * pid=%d listening for EAS", p_base_pid->i_pid ); } } static void ATSC_STT_Callback( void *p_cb_basepid, dvbpsi_atsc_stt_t* p_stt ) { ts_pid_t *p_base_pid = (ts_pid_t *) p_cb_basepid; if( unlikely(p_base_pid->type != TYPE_PSIP || p_base_pid->i_pid != ATSC_BASE_PID) ) { assert( p_base_pid->type == TYPE_PSIP ); assert( p_base_pid->i_pid == ATSC_BASE_PID ); dvbpsi_atsc_DeleteSTT( p_stt ); return; } demux_t *p_demux = (demux_t *) p_base_pid->u.p_psip->handle->p_sys; ts_psip_context_t *p_ctx = p_base_pid->u.p_psip->p_ctx; dvbpsi_t *p_handle = p_base_pid->u.p_psip->handle; if( !p_ctx->p_stt ) /* First call */ { if( !ATSC_ATTACH( p_handle, MGT, ATSC_MGT_TABLE_ID, 0x00, p_base_pid ) ) { msg_Err( p_demux, "Can't attach MGT decoder to pid %d", ATSC_BASE_PID ); ATSC_Detach_Dvbpsi_Decoders( p_handle ); dvbpsi_atsc_DeleteSTT( p_ctx->p_stt ); p_stt = NULL; } } else { dvbpsi_atsc_DeleteSTT( p_ctx->p_stt ); } if( p_stt ) { time_t i_current_time = atsc_a65_GPSTimeToEpoch( p_stt->i_system_time, p_stt->i_gps_utc_offset ); EIT_DEBUG_TIMESHIFT( i_current_time ); p_demux->p_sys->i_network_time = i_current_time; p_demux->p_sys->i_network_time_update = time(NULL); es_out_Control( p_demux->out, ES_OUT_SET_EPG_TIME, p_demux->p_sys->i_network_time ); } p_ctx->p_stt = p_stt; } static void ATSC_STT_RawCallback( dvbpsi_t *p_handle, const dvbpsi_psi_section_t* p_section, void *p_base_pid ) { VLC_UNUSED( p_handle ); for( ; p_section ; p_section = p_section->p_next ) { dvbpsi_atsc_stt_t *p_stt = DVBPlague_STT_Decode( p_section ); if( p_stt ) /* Send to real callback */ ATSC_STT_Callback( p_base_pid, p_stt ); } } bool ATSC_Attach_Dvbpsi_Base_Decoders( dvbpsi_t *p_handle, void *p_base_pid ) { if( !ATSC_ATTACH_WITH_FIXED_DECODER( p_handle, STT, ATSC_STT_TABLE_ID, 0x00, p_base_pid ) ) { ATSC_Detach_Dvbpsi_Decoders( p_handle ); /* shouldn't be any, except demux */ return false; } return true; } static void ATSC_NewTable_Callback( dvbpsi_t *p_dvbpsi, uint8_t i_table_id, uint16_t i_extension, void *p_cb_pid ) { 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 = 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: if( !ATSC_ATTACH_WITH_FIXED_DECODER( p_dvbpsi, ETT, ATSC_ETT_TABLE_ID, i_extension, p_cb_pid ) ) msg_Warn( p_demux, "Cannot attach ETT decoder source %" PRIu16, i_extension ); break; case ATSC_EIT_TABLE_ID: if( !ATSC_ATTACH( p_dvbpsi, EIT, ATSC_EIT_TABLE_ID, i_extension, p_cb_pid ) ) msg_Warn( p_demux, "Cannot attach EIT decoder source %" PRIu16, i_extension ); break; default: break; } }