Commit 3014d574 authored by Francois Cartegnie's avatar Francois Cartegnie

demux: ts: add support for psip tables

parent ab7c36a7
...@@ -232,6 +232,9 @@ libts_plugin_la_SOURCES = demux/mpeg/ts.c demux/mpeg/ts.h \ ...@@ -232,6 +232,9 @@ libts_plugin_la_SOURCES = demux/mpeg/ts.c demux/mpeg/ts.h \
demux/mpeg/ts_pid.h demux/mpeg/ts_pid.c \ demux/mpeg/ts_pid.h demux/mpeg/ts_pid.c \
demux/mpeg/ts_psi.h demux/mpeg/ts_psi.c \ demux/mpeg/ts_psi.h demux/mpeg/ts_psi.c \
demux/mpeg/ts_psi_eit.h demux/mpeg/ts_psi_eit.c \ demux/mpeg/ts_psi_eit.h demux/mpeg/ts_psi_eit.c \
demux/mpeg/ts_psip.h demux/mpeg/ts_psip.c \
demux/mpeg/ts_psip_dvbpsi_fixes.h demux/mpeg/ts_psip_dvbpsi_fixes.c \
demux/mpeg/ts_decoders.h demux/mpeg/ts_decoders.c \
demux/mpeg/ts_streams.h demux/mpeg/ts_streams.c \ demux/mpeg/ts_streams.h demux/mpeg/ts_streams.c \
demux/mpeg/ts_scte.h demux/mpeg/ts_scte.c \ demux/mpeg/ts_scte.h demux/mpeg/ts_scte.c \
demux/mpeg/sections.c demux/mpeg/sections.h \ demux/mpeg/sections.c demux/mpeg/sections.h \
...@@ -249,6 +252,7 @@ libts_plugin_la_SOURCES = demux/mpeg/ts.c demux/mpeg/ts.h \ ...@@ -249,6 +252,7 @@ libts_plugin_la_SOURCES = demux/mpeg/ts.c demux/mpeg/ts.h \
mux/mpeg/tables.c mux/mpeg/tables.h \ mux/mpeg/tables.c mux/mpeg/tables.h \
mux/mpeg/tsutil.c mux/mpeg/tsutil.h \ mux/mpeg/tsutil.c mux/mpeg/tsutil.h \
codec/scte18.h \ codec/scte18.h \
codec/atsc_a65.c codec/atsc_a65.h \
codec/opus_header.c codec/opus_header.c
libts_plugin_la_CFLAGS = $(AM_CFLAGS) $(DVBPSI_CFLAGS) libts_plugin_la_CFLAGS = $(AM_CFLAGS) $(DVBPSI_CFLAGS)
libts_plugin_la_LIBADD = $(DVBPSI_LIBS) $(SOCKET_LIBS) libts_plugin_la_LIBADD = $(DVBPSI_LIBS) $(SOCKET_LIBS)
......
...@@ -129,6 +129,8 @@ static const char *const arib_mode_list_text[] = ...@@ -129,6 +129,8 @@ static const char *const arib_mode_list_text[] =
"Forces ARIB STD-B24 mode for decoding characters." \ "Forces ARIB STD-B24 mode for decoding characters." \
"This feature affects EPG information and subtitles." ) "This feature affects EPG information and subtitles." )
#define ATSC_MODE_TEXT N_("ATSC")
vlc_module_begin () vlc_module_begin ()
set_description( N_("MPEG Transport Stream demuxer") ) set_description( N_("MPEG Transport Stream demuxer") )
set_shortname ( "MPEG-TS" ) set_shortname ( "MPEG-TS" )
...@@ -155,7 +157,7 @@ vlc_module_begin () ...@@ -155,7 +157,7 @@ vlc_module_begin ()
add_integer( "ts-arib", ARIBMODE_AUTO, SUPPORT_ARIB_TEXT, SUPPORT_ARIB_LONGTEXT, false ) add_integer( "ts-arib", ARIBMODE_AUTO, SUPPORT_ARIB_TEXT, SUPPORT_ARIB_LONGTEXT, false )
change_integer_list( arib_mode_list, arib_mode_list_text ) change_integer_list( arib_mode_list, arib_mode_list_text )
add_bool( "ts-eas", false, SCTE18_DESCRIPTION, NULL, false ) add_bool( "ts-atsc", false, ATSC_MODE_TEXT, NULL, false )
add_obsolete_bool( "ts-silent" ); add_obsolete_bool( "ts-silent" );
...@@ -519,7 +521,12 @@ static int Open( vlc_object_t *p_this ) ...@@ -519,7 +521,12 @@ static int Open( vlc_object_t *p_this )
p_sys->b_canfastseek = false; p_sys->b_canfastseek = false;
p_sys->b_force_seek_per_percent = var_InheritBool( p_demux, "ts-seek-percent" ); p_sys->b_force_seek_per_percent = var_InheritBool( p_demux, "ts-seek-percent" );
p_sys->b_atsc_eas = var_InheritBool( p_demux, "ts-eas" ); p_sys->b_atsc = var_InheritBool( p_demux, "ts-atsc" );
if( !p_sys->b_atsc )
{
p_sys->b_atsc = !strcmp( p_demux->psz_access, "atsc" ) ||
!strcmp( p_demux->psz_access, "usdigital" );
}
p_sys->arib.e_mode = var_InheritInteger( p_demux, "ts-arib" ); p_sys->arib.e_mode = var_InheritInteger( p_demux, "ts-arib" );
stream_Control( p_sys->stream, STREAM_CAN_SEEK, &p_sys->b_canseek ); stream_Control( p_sys->stream, STREAM_CAN_SEEK, &p_sys->b_canseek );
...@@ -714,6 +721,12 @@ static int Demux( demux_t *p_demux ) ...@@ -714,6 +721,12 @@ static int Demux( demux_t *p_demux )
block_Release( p_pkt ); block_Release( p_pkt );
break; break;
case TYPE_PSIP:
if( p_pid->u.p_psip->handle->p_decoder )
dvbpsi_packet_push( p_pid->u.p_psip->handle, p_pkt->p_buffer );
block_Release( p_pkt );
break;
default: default:
/* We have to handle PCR if present */ /* We have to handle PCR if present */
PCRHandle( p_demux, p_pid, p_pkt ); PCRHandle( p_demux, p_pid, p_pkt );
......
...@@ -57,7 +57,7 @@ struct demux_sys_t ...@@ -57,7 +57,7 @@ struct demux_sys_t
bool b_force_seek_per_percent; bool b_force_seek_per_percent;
bool b_atsc_eas; bool b_atsc;
struct struct
{ {
arib_modes_e e_mode; arib_modes_e e_mode;
......
...@@ -172,6 +172,13 @@ bool PIDSetup( demux_t *p_demux, ts_pid_type_t i_type, ts_pid_t *pid, ts_pid_t * ...@@ -172,6 +172,13 @@ bool PIDSetup( demux_t *p_demux, ts_pid_type_t i_type, ts_pid_t *pid, ts_pid_t *
return false; return false;
break; break;
case TYPE_PSIP:
PIDReset( pid );
pid->u.p_psip = ts_psip_New( p_demux );
if( !pid->u.p_psip )
return false;
break;
default: default:
assert(false); assert(false);
break; break;
...@@ -241,6 +248,11 @@ void PIDRelease( demux_t *p_demux, ts_pid_t *pid ) ...@@ -241,6 +248,11 @@ void PIDRelease( demux_t *p_demux, ts_pid_t *pid )
ts_psi_Del( p_demux, pid->u.p_psi ); ts_psi_Del( p_demux, pid->u.p_psi );
pid->u.p_psi = NULL; pid->u.p_psi = NULL;
break; break;
case TYPE_PSIP:
ts_psip_Del( p_demux, pid->u.p_psip );
pid->u.p_psip = NULL;
break;
} }
SetPIDFilter( p_demux->p_sys, pid, false ); SetPIDFilter( p_demux->p_sys, pid, false );
......
...@@ -35,6 +35,7 @@ typedef enum ...@@ -35,6 +35,7 @@ typedef enum
TYPE_SDT, TYPE_SDT,
TYPE_TDT, TYPE_TDT,
TYPE_EIT, TYPE_EIT,
TYPE_PSIP,
} ts_pid_type_t; } ts_pid_type_t;
enum enum
...@@ -65,6 +66,7 @@ struct ts_pid_t ...@@ -65,6 +66,7 @@ struct ts_pid_t
ts_pmt_t *p_pmt; ts_pmt_t *p_pmt;
ts_pes_t *p_pes; ts_pes_t *p_pes;
ts_psi_t *p_psi; ts_psi_t *p_psi;
ts_psip_t *p_psip;
} u; } u;
struct struct
......
...@@ -49,6 +49,7 @@ ...@@ -49,6 +49,7 @@
#include "sections.h" #include "sections.h"
#include "ts_sl.h" #include "ts_sl.h"
#include "ts_scte.h" #include "ts_scte.h"
#include "ts_psip.h"
#include <assert.h> #include <assert.h>
...@@ -1449,10 +1450,6 @@ void PMTCallBack( void *data, dvbpsi_pmt_t *p_dvbpsipmt ) ...@@ -1449,10 +1450,6 @@ void PMTCallBack( void *data, dvbpsi_pmt_t *p_dvbpsipmt )
dvbpsi_pmt_es_t *p_dvbpsies; dvbpsi_pmt_es_t *p_dvbpsies;
for( p_dvbpsies = p_dvbpsipmt->p_first_es; p_dvbpsies != NULL; p_dvbpsies = p_dvbpsies->p_next ) for( p_dvbpsies = p_dvbpsipmt->p_first_es; p_dvbpsies != NULL; p_dvbpsies = p_dvbpsies->p_next )
{ {
/* Do not mix with arbitrary pid if any */
if( p_sys->b_atsc_eas && p_dvbpsies->i_pid == SCTE18_SI_BASE_PID )
continue;
ts_pid_t *pespid = GetPID(p_sys, p_dvbpsies->i_pid); ts_pid_t *pespid = GetPID(p_sys, p_dvbpsies->i_pid);
if ( pespid->type != TYPE_PES && pespid->type != TYPE_FREE ) if ( pespid->type != TYPE_PES && pespid->type != TYPE_FREE )
{ {
...@@ -1610,24 +1607,54 @@ void PMTCallBack( void *data, dvbpsi_pmt_t *p_dvbpsipmt ) ...@@ -1610,24 +1607,54 @@ void PMTCallBack( void *data, dvbpsi_pmt_t *p_dvbpsipmt )
} }
/* Add arbitrary PID from here */ /* Add arbitrary PID from here */
if ( p_sys->b_atsc_eas && p_pmt->e_streams.i_size ) if ( registration_type == TS_PMT_REGISTRATION_ATSC || p_sys->b_atsc )
{ {
ts_pid_t *easpid = GetPID(p_sys, SCTE18_SI_BASE_PID); ts_pid_t *atsc_base_pid = GetPID(p_sys, ATSC_BASE_PID);
if ( PIDSetup( p_demux, TYPE_PES, easpid, pmtpid ) ) if ( PIDSetup( p_demux, TYPE_PSIP, atsc_base_pid, pmtpid ) )
{
ts_psip_t *p_psip = atsc_base_pid->u.p_psip;
if( !ATSC_Attach_Dvbpsi_Base_Decoders( p_psip->handle, atsc_base_pid ) )
{
msg_Err( p_demux, "dvbpsi_atsc_AttachMGT/STT failed for program %d",
p_pmt->i_number );
PIDRelease( p_demux, atsc_base_pid );
}
else
{
p_pmt->p_mgt = atsc_base_pid;
SetPIDFilter( p_demux->p_sys, atsc_base_pid, true );
msg_Dbg( p_demux, " * pid=%d listening for MGT/STT", atsc_base_pid->i_pid );
/* Set up EAS spu es */
if( p_pmt->e_streams.i_size )
{
ts_pes_es_t *p_eas_es = ts_pes_es_New( p_pmt );
if( likely(p_eas_es) )
{
p_eas_es->fmt.i_codec = VLC_CODEC_SCTE_18;
p_eas_es->fmt.i_cat = SPU_ES;
p_eas_es->fmt.i_id = ATSC_BASE_PID;
p_eas_es->fmt.i_group = p_pmt->i_number;
p_eas_es->fmt.psz_description = strdup(SCTE18_DESCRIPTION);
if( p_psip->p_eas_es )
{
ts_pes_es_t *p_next = p_psip->p_eas_es->p_next;
p_psip->p_eas_es->p_next = p_eas_es;
p_eas_es->p_next = p_next;
}
else
{
p_psip->p_eas_es = p_eas_es;
}
msg_Dbg( p_demux, " * pid=%d listening for EAS events", ATSC_BASE_PID );
}
}
}
}
else if( atsc_base_pid->type != TYPE_FREE )
{ {
ARRAY_APPEND( p_pmt->e_streams, easpid ); msg_Err( p_demux, "can't attach PSIP table handlers"
ts_pes_t *p_easpes = easpid->u.p_pes; "on already in use ATSC base pid %d", ATSC_BASE_PID );
p_easpes->data_type = TS_ES_DATA_TABLE_SECTION;
p_easpes->p_es->fmt.i_codec = VLC_CODEC_SCTE_18;
p_easpes->p_es->fmt.i_cat = SPU_ES;
p_easpes->p_es->fmt.i_id = SCTE18_SI_BASE_PID;
p_easpes->p_es->fmt.i_group = p_pmt->i_number;
p_easpes->p_es->fmt.psz_description = strdup(SCTE18_DESCRIPTION);
p_easpes->b_always_receive = true;
ts_sections_processor_Add( &p_easpes->p_sections_proc,
SCTE18_TABLE_ID, 0x00,
false, SCTE18_Section_Handler );
msg_Dbg( p_demux, " * pid=%d listening for EAS events", easpid->i_pid );
} }
} }
......
/*****************************************************************************
* 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 <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
#include <vlc_demux.h>
#include <vlc_meta.h>
#include <vlc_epg.h>
#ifndef _DVBPSI_DVBPSI_H_
#include <dvbpsi/dvbpsi.h>
#endif
#ifndef _DVBPSI_DEMUX_H_
#include <dvbpsi/demux.h>
#endif
#include <dvbpsi/descriptor.h>
#include <dvbpsi/atsc_mgt.h>
#include <dvbpsi/atsc_vct.h>
#include <dvbpsi/atsc_eit.h>
#include <dvbpsi/atsc_ett.h>
#include <dvbpsi/atsc_stt.h>
#include <dvbpsi/dr_a0.h>
/* Custom decoders */
#include <dvbpsi/psi.h>
#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 <assert.h>
/*
* 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_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 */
};
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_stt = NULL;
p_ctx->p_vct = NULL;
p_ctx->p_a65 = NULL;
}
return p_ctx;
}
void ts_psip_context_Delete( ts_psip_context_t *p_ctx )
{
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 );
free( p_ctx );
}
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 > 6 )
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 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_ctx = 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) )
{
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 ) )
{
msg_Warn( p_demux, "Received EIT for unkown channel %d", p_eit->i_source_id );
dvbpsi_atsc_DeleteEIT( p_eit );
return;
}
/* 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 );
EIT_DEBUG_TIMESHIFT( i_current_time );
vlc_epg_t *p_epg = vlc_epg_New( NULL );
if( !p_epg )
{
dvbpsi_atsc_DeleteEIT( p_eit );
return;
}
if( !p_ctx->p_a65 && !(p_ctx->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;
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 );
/* 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 ) */
if( i_current_event_start_time )
vlc_epg_SetCurrent( p_epg, i_current_event_start_time );
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 );
dvbpsi_atsc_DeleteEIT( p_eit );
}
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( const 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:
{
const dvbpsi_extended_channel_name_dr_t *p_ecndr =
(const dvbpsi_extended_channel_name_dr_t *)p_dr;
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 );
}
}
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 );
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 );
}
}
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_SectionsCallback, p_base_pid ) )
{
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 )
{
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 );
}
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 );
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 = GetPID(p_demux->p_sys, ATSC_BASE_PID);
if( !p_base_pid->u.p_psip->p_ctx->p_vct )
return;
switch( i_table_id )
{
case ATSC_EIT_TABLE_ID:
ATSC_ATTACH( p_dvbpsi, EIT, ATSC_EIT_TABLE_ID, i_extension, p_cb_pid );
break;
default:
break;
}
}
/*****************************************************************************
* ts_psip.h : 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 <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifndef VLC_ATSC_PSIP_H
#define VLC_ATSC_PSIP_H
#ifndef _DVBPSI_DVBPSI_H_
#include <dvbpsi/dvbpsi.h>
#endif
//#define ATSC_DEBUG_EIT /* Will also shift EPG to current time */
#define ATSC_EIT_MAX_DEPTH_MIN1 3 /* Only keep 4*3 hours tables */
#define ATSC_TABLE_TYPE_TVCT 0x0000
#define ATSC_TABLE_TYPE_TVCT_NEXT 0x0001
#define ATSC_TABLE_TYPE_CVCT 0x0002
#define ATSC_TABLE_TYPE_CVCT_NEXT 0x0003
#define ATSC_TABLE_TYPE_ETT 0x0004
#define ATSC_TABLE_TYPE_DCCSCT 0x0005
#define ATSC_TABLE_TYPE_EIT_0 0x0100
#define ATSC_TABLE_TYPE_EIT_127 0x017F
#define ATSC_TABLE_TYPE_ETT_0 0x0200
#define ATSC_TABLE_TYPE_ETT_127 0x027F
#define ATSC_TABLE_TYPE_RTT_1 0x0301
#define ATSC_TABLE_TYPE_RTT_255 0x03FF
#define ATSC_TABLE_TYPE_DCCT_00 0x1400
#define ATSC_TABLE_TYPE_DCCT_FF 0x14FF
#define ATSC_BASE_PID 0x1FFB
#define ATSC_MGT_TABLE_ID 0xC7
#define ATSC_TVCT_TABLE_ID 0xC8
#define ATSC_CVCT_TABLE_ID 0xC9
#define ATSC_RRT_TABLE_ID 0xCA
#define ATSC_EIT_TABLE_ID 0xCB
#define ATSC_ETT_TABLE_ID 0xCC
#define ATSC_STT_TABLE_ID 0xCD
#define ATSC_DESCRIPTOR_CONTENT_ADVISORY 0x87
#define ATSC_DESCRIPTOR_EXTENDED_CHANNEL_NAME 0xA0
typedef struct ts_psip_context_t ts_psip_context_t;
ts_psip_context_t * ts_psip_context_New( void );
void ts_psip_context_Delete( ts_psip_context_t * );
bool ATSC_Attach_Dvbpsi_Base_Decoders( dvbpsi_t *p_handle, void *p_cb_data );
void ATSC_Detach_Dvbpsi_Decoders( dvbpsi_t *p_handle );
#endif
...@@ -24,6 +24,11 @@ ...@@ -24,6 +24,11 @@
#include <vlc_demux.h> #include <vlc_demux.h>
#include <vlc_es.h> #include <vlc_es.h>
#ifndef _DVBPSI_DVBPSI_H_
#include <dvbpsi/dvbpsi.h>
#endif
#include <dvbpsi/psi.h>
#include "ts_pid.h" #include "ts_pid.h"
#include "ts_scte.h" #include "ts_scte.h"
#include "ts_streams_private.h" #include "ts_streams_private.h"
...@@ -31,25 +36,40 @@ ...@@ -31,25 +36,40 @@
#include <assert.h> #include <assert.h>
void SCTE18_Section_Handler( demux_t *p_demux, ts_pid_t *pid, block_t *p_content ) /* EAS Handling */
void SCTE18_SectionsCallback( dvbpsi_t *p_handle, const dvbpsi_psi_section_t* p_section,
void *p_base_pid )
{ {
assert( pid->u.p_pes->p_es->fmt.i_codec == VLC_CODEC_SCTE_18 ); demux_t *p_demux = (demux_t *) p_handle->p_sys;
ts_pmt_t *p_pmt = pid->u.p_pes->p_es->p_program; ts_pid_t *p_pid = (ts_pid_t *) p_base_pid;
mtime_t i_date = TimeStampWrapAround( p_pmt->pcr.i_first, p_pmt->pcr.i_current ); ts_psip_t *p_psip = p_pid->u.p_psip;
if( p_pid->type != TYPE_PSIP || !p_psip->p_eas_es )
return;
int i_priority = scte18_get_EAS_priority( p_content->p_buffer, p_content->i_buffer ); for( ; p_section; p_section = p_section->p_next )
msg_Dbg( p_demux, "Received EAS Alert with priority %d", i_priority );
/* We need to extract the truncated pts stored inside the payload */
ts_pes_es_t *p_es = pid->u.p_pes->p_es;
if( p_es->id )
{ {
if( i_priority == EAS_PRIORITY_HIGH || i_priority == EAS_PRIORITY_MAX ) size_t i_payload = p_section->p_payload_end - p_section->p_payload_start;
const int i_priority = scte18_get_EAS_priority( p_section->p_payload_start, i_payload );
msg_Dbg( p_demux, "Received EAS Alert with priority %d", i_priority );
if( i_priority != EAS_PRIORITY_HIGH && i_priority != EAS_PRIORITY_MAX )
continue;
for( ts_pes_es_t *p_es = p_psip->p_eas_es; p_es; p_es = p_es->p_next )
{
if( !p_es->id && !(p_es->id = es_out_Add( p_demux->out, &p_es->fmt )) )
continue;
const ts_pmt_t *p_pmt = p_es->p_program;
const mtime_t i_date = TimeStampWrapAround( p_pmt->pcr.i_first, p_pmt->pcr.i_current );
block_t *p_block = block_Alloc( p_section->p_payload_end - p_section->p_payload_start );
memcpy( p_block->p_buffer, p_section->p_payload_start, i_payload );
p_block->i_dts = p_block->i_pts = FROM_SCALE( i_date );
es_out_Control( p_demux->out, ES_OUT_SET_ES_STATE, p_es->id, true ); es_out_Control( p_demux->out, ES_OUT_SET_ES_STATE, p_es->id, true );
p_content->i_dts = p_content->i_pts = FROM_SCALE( i_date ); es_out_Send( p_demux->out, p_es->id, p_block );
es_out_Send( p_demux->out, p_es->id, p_content ); }
} }
else
block_Release( p_content );
} }
void SCTE27_Section_Handler( demux_t *p_demux, ts_pid_t *pid, block_t *p_content ) void SCTE27_Section_Handler( demux_t *p_demux, ts_pid_t *pid, block_t *p_content )
......
...@@ -21,7 +21,9 @@ ...@@ -21,7 +21,9 @@
#include "../../codec/scte18.h" #include "../../codec/scte18.h"
void SCTE18_Section_Handler( demux_t *p_demux, ts_pid_t *pid, block_t *p_content ); void SCTE18_SectionsCallback( dvbpsi_t *p_handle,
const dvbpsi_psi_section_t* p_section,
void *p_cb_data );
void SCTE27_Section_Handler( demux_t *p_demux, ts_pid_t *pid, block_t *p_content ); void SCTE27_Section_Handler( demux_t *p_demux, ts_pid_t *pid, block_t *p_content );
#endif #endif
...@@ -29,7 +29,9 @@ ...@@ -29,7 +29,9 @@
#ifndef _DVBPSI_DVBPSI_H_ #ifndef _DVBPSI_DVBPSI_H_
#include <dvbpsi/dvbpsi.h> #include <dvbpsi/dvbpsi.h>
#endif #endif
#include <dvbpsi/demux.h> #ifndef _DVBPSI_DEMUX_H_
#include <dvbpsi/demux.h>
#endif
#include <dvbpsi/descriptor.h> #include <dvbpsi/descriptor.h>
#include <dvbpsi/pat.h> #include <dvbpsi/pat.h>
#include <dvbpsi/pmt.h> #include <dvbpsi/pmt.h>
...@@ -43,6 +45,8 @@ ...@@ -43,6 +45,8 @@
#include "ts_pid.h" #include "ts_pid.h"
#include "ts.h" #include "ts.h"
#include "ts_psip.h"
static inline bool handle_Init( demux_t *p_demux, dvbpsi_t **handle ) static inline bool handle_Init( demux_t *p_demux, dvbpsi_t **handle )
{ {
*handle = dvbpsi_new( &dvbpsi_messages, DVBPSI_MSG_DEBUG ); *handle = dvbpsi_new( &dvbpsi_messages, DVBPSI_MSG_DEBUG );
...@@ -303,3 +307,48 @@ void ts_psi_Del( demux_t *p_demux, ts_psi_t *psi ) ...@@ -303,3 +307,48 @@ void ts_psi_Del( demux_t *p_demux, ts_psi_t *psi )
dvbpsi_delete( psi->handle ); dvbpsi_delete( psi->handle );
free( psi ); free( psi );
} }
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; i<psip->eit.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;
}
...@@ -24,6 +24,7 @@ typedef struct ts_pat_t ts_pat_t; ...@@ -24,6 +24,7 @@ typedef struct ts_pat_t ts_pat_t;
typedef struct ts_pmt_t ts_pmt_t; typedef struct ts_pmt_t ts_pmt_t;
typedef struct ts_pes_t ts_pes_t; typedef struct ts_pes_t ts_pes_t;
typedef struct ts_psi_t ts_psi_t; typedef struct ts_psi_t ts_psi_t;
typedef struct ts_psip_t ts_psip_t;
/* Structs */ /* Structs */
ts_pat_t *ts_pat_New( demux_t * ); ts_pat_t *ts_pat_New( demux_t * );
...@@ -44,4 +45,7 @@ void ts_pes_Del( demux_t *, ts_pes_t * ); ...@@ -44,4 +45,7 @@ void ts_pes_Del( demux_t *, ts_pes_t * );
ts_psi_t *ts_psi_New( demux_t * ); ts_psi_t *ts_psi_New( demux_t * );
void ts_psi_Del( demux_t *, ts_psi_t * ); void ts_psi_Del( demux_t *, ts_psi_t * );
ts_psip_t *ts_psip_New( demux_t * );
void ts_psip_Del( demux_t *, ts_psip_t * );
#endif #endif
...@@ -117,10 +117,14 @@ struct ts_psi_t ...@@ -117,10 +117,14 @@ struct ts_psi_t
}; };
typedef struct ts_psip_context_t ts_psip_context_t;
struct ts_psip_t struct ts_psip_t
{ {
dvbpsi_t *handle; dvbpsi_t *handle;
int i_version; int i_version;
ts_pes_es_t *p_eas_es;
ts_psip_context_t *p_ctx;
DECL_ARRAY(ts_pid_t *) eit; 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